How do I use Sourcegraph with Go?
How do I use Sourcegraph with Go?
Satish Talim
Buy on Leanpub

Preface

Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.

Who is the booklet for?

This short booklet will show Go newbies how one can use Sourcegraph to better write one’s Go programs. To try out the programs in this booklet, you should have a working copy of Go 1.2 on your computer.

Acknowledgements

I would like to thank Sourcegraph for permiting me to write this booklet.

Using Code Examples

All of the code in this booklet can be used pretty much anywhere and anyhow you please.

How to Contact Me

I can be reached via e-mail at satish.talim@gmail.com. Please contact me if you have any questions, comments, kudos or criticism on the booklet. Constructive criticism is definitely appreciated; I want this booklet to get better through your feedback.

Thanks

Thanks for downloading and checking out this booklet. As part of the lean publishing philosophy, you’ll be able to interact with me as the booklet is completed. I’ll be able to change things, reorganize parts, and generally make a better booklet. I hope you enjoy.

1 What’s Sourcegraph?

Sourcegraph is a code search engine that shows you documentation and real-world usage examples for hundreds of thousands of libraries written in Go and Ruby.

1.1 Getting Started

Sign up with your GitHub account (no private data is requested). Signing in is optional, but it helps Sourcegraph find all of your open-source code and attribute it to you.

1.2 How Do I Use It?

We shall build a small Go program and use Sourcegraph along the way. This simple application: given a GitHub username, it fetches that person’s company and full name.

1.3 Assumptions

I am assuming that you have:

  • downloaded and installed Go.
  • set the environment variable GOROOT.
  • set up the workspace and environment variable GOPATH.
  • updated the system environment variable PATH to include your workspace bin subdirectory.
  • setup the package path. I have it set to c:\go_projects\go\src\github.com\SatishTalim

Open a command window, make a new folder and cd to it as follows:

$ mkdir $GOPATH/src/github.com/SatishTalim/sourcegraph
$ cd $GOPATH/src/github.com/SatishTalim/sourcegraph

If you are new to Go, you can download my free eBook that covers all of the above.

1.4 sourcegraph.go - Outline 1

Let’s get started. I have a very basic outline of the code sourcegraph.go.

Program sourcegraph.go


package main

func main() {
	// TODO
}

First, I want to make a Http request to the GitHub API. Next I would like to parse the JSON response and determine that person’s name and company.

I want to take some user input using the flag package. However, I haven’t used this package much and bad at remembering how to exactly use it. I would definitely like to know how. So, let us look it up on sourcegraph.

Let’s type flag golang as shown below:

Sourcegraph

Sourcegraph

If you see package flag there are over 33,000 examples of usage of flag on sourcegraph. That’s good. Let’s click on that.

In the image below, you can see the functions and other definitions in the package flag. On the right you can see how many times it has been used by other people.

Sourcegraph

Sourcegraph

Click on the Examples link on the left and see some quick examples of how it is used.

Scroll down and see which example is similar to the one you want to write. I think marvin.go seems to be what I want.

Sourcegraph

Sourcegraph

Click on marvin.go to load the full example as seen in the image below.

Sourcegraph

Sourcegraph

I think I will use flag.String and flag.Parse() in my program sourcegraph.go.

1.5 sourcegraph.go - Outline 2

Let’s type in the program as follows:

Program sourcegraph.go


package main

import (
	"flag"
	"log"
)

func main() {
	login := flag.String("login", "", "GitHub login of user")
	flag.Parse()

	if login == "" {
		log.Fatal("must specify login")
	}

	log.Println("Looking up GitHub user: ", login)
}

In the program above, we are using the flag package to read in the command line arguments.

Next, in the same folder where the program is located, type:

$ go run sourcegraph.go

Oh! We get an error that says:

login == "" (mismatched types *string and string)

If you re-check the image below, you will find that we need to de-reference the variable (see *config):

Sourcegraph

Sourcegraph

1.6 sourcegraph.go - Outline 3

Our modified program is:

Program sourcegraph.go


package main

import (
	"flag"
	"log"
)

func main() {
	login := flag.String("login", "", "GitHub login of user")
	flag.Parse()

	if *login == "" {
		log.Fatal("must specify login")
	}

	log.Println("Looking up GitHub user: ", *login)
}

Let us re-run our program:

$ go run sourcegraph.go
2014/06/03 09:49:50 must specify login

must specify login great! Okay, let us do that with my login SatishTalim:

$ go run sourcegraph.go -login=SatishTalim
2014/06/03 09:45:30 Looking up GitHub user:  SatishTalim

Cool! It’s working. Now, let us write the code that fetches from the GitHub API. Let’s go back to Sourcegraph and see how to do a get.

Sourcegraph

Sourcegraph

Let’s click on http.Get. We should see as shown in the image below:

Sourcegraph

Sourcegraph

In the image, we can see the documentation, the http.Get being used by 405 programmers and so on. Let’s click on Examples on the left.

Sourcegraph

Sourcegraph

I think the highlighted text seems like a simple example, which I will copy/paste into my program.

1.7 sourcegraph.go - Outline 4

Having already looked up the GitHub API documentation, I have edited the copied code slightly as in:

Program sourcegraph.go


package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
)

func main() {
	login := flag.String("login", "", "GitHub login of user")
	flag.Parse()

	if *login == "" {
		log.Fatal("must specify login")
	}

	log.Println("Looking up GitHub user: ", *login)
	
	resp, err := http.Get(fmt.Sprintf("http://api.github.com/users/%s", *login))
	if err != nil {
		log.Fatal(err)
	}
}

Note that I have added two libraries namely fmt and net/http. In this program we are able to get the response resp but how do we get it’s contents?

We had copied/pasted the code from the websocket_test.go program. Let’s go back to its full program listing and check if we find what to do with resp. You will soon realize that there’s nothing related to resp there. Let us go back to the other examples listed where websocket_test.go is. Scrolling down, let us check docker/utils/utils.go. Oh! Nothing here. On the next page I plan to look at hipchat/hipchat.go as shown below:

Sourcegraph

Sourcegraph

I observe that I can extract the resp.Body out of resp and then use json.Unmarshal to extract the contents namely the person’s name and company. Observe that json.Unmarshal extracts the information into a variable. We need to provide a variable where the JSON package can put the decoded data. This map[string]interface{} will hold a map of strings to arbitrary data types.

1.8 sourcegraph.go - Outline 5

Let us copy/paste the relevant code from hipchat/hipchat.go into our program:

Program sourcegraph.go


package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	login := flag.String("login", "", "GitHub login of user")
	flag.Parse()

	if *login == "" {
		log.Fatal("must specify login")
	}

	log.Println("Looking up GitHub user: ", *login)
	
	resp, err := http.Get(fmt.Sprintf("https://api.github.com/users/%s", *login))
	if err != nil {
		log.Fatal(err)
	}
	
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	
	var userdata map[string]interface{}
	err = json.Unmarshal(body, &userdata)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(userdata)
}

Remember to import the package io/ioutil and the last statement log.Println(userdata) just prints the data.

Let us run our program:

$ go run sourcegraph.go -login=SatishTalim
2014/06/04 09:57:34 Looking up GitHub user:  SatishTalim
2014/06/04 09:57:35 map[organizations_url:https://api.github.com/users/SatishTal
im/orgs type:User hireable:false bio:Founder RubyLearning public_gists:40 avatar
_url:https://avatars.githubusercontent.com/u/1052069? followers_url:https://api.
github.com/users/SatishTalim/followers location:India public_repos:26 followers:
67 gists_url:https://api.github.com/users/SatishTalim/gists{/gist_id} created_at
:2011-09-15T03:47:18Z login:SatishTalim url:https://api.github.com/users/SatishT
alim starred_url:https://api.github.com/users/SatishTalim/starred{/owner}{/repo}
 repos_url:https://api.github.com/users/SatishTalim/repos name:Satish Talim comp
any:RubyLearning subscriptions_url:https://api.github.com/users/SatishTalim/subs
criptions email:satish@rubylearning.org id:1.052069e+06 html_url:https://github.
com/SatishTalim following_url:https://api.github.com/users/SatishTalim/following
{/other_user} following:7 gravatar_id:e78a9914ef2e253f5af78768600f9193 blog:http
://rubylearning.org/ events_url:https://api.github.com/users/SatishTalim/events{
/privacy} received_events_url:https://api.github.com/users/SatishTalim/received_
events site_admin:false updated_at:2014-06-04T04:15:11Z]

You can see the API response above, but we want to show some specific pieces of information. We can see the name and company in that data.

1.9 sourcegraph.go - Final program

Instead of printing userdata we will print only the required data as shown below:

Program sourcegraph.go


package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	login := flag.String("login", "", "GitHub login of user")
	flag.Parse()

	if *login == "" {
		log.Fatal("must specify login")
	}

	log.Println("Looking up GitHub user: ", *login)
	
	resp, err := http.Get(fmt.Sprintf("https://api.github.com/users/%s", *login))
	if err != nil {
		log.Fatal(err)
	}
	
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	
	var userdata map[string]interface{}
	err = json.Unmarshal(body, &userdata)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("User's full name: ", userdata["name"])
	log.Println("User's company: ", userdata["company"])
}

Re-run the program:

$ go run sourcegraph.go -login=SatishTalim
2014/06/04 10:08:56 Looking up GitHub user:  SatishTalim
2014/06/04 10:08:57 User's full name:  Satish Talim
2014/06/04 10:08:57 User's company:  RubyLearning

That’s my data and it’s correct. Let us try another user:

$ go run sourcegraph.go -login=beyang
2014/06/04 10:11:43 Looking up GitHub user:  beyang
2014/06/04 10:11:44 User's full name:  Beyang Liu
2014/06/04 10:11:44 User's company:  Sourcegraph

That’s it! So searching on Sourcegraph you can quickly see how other programmers are doing similar work that we are doing. We thus save a lot of time by being able to look at code and see how things are actually used, instead of having to read thro’ docs. Documentation is great and Sourcegraph is a great way to find docs quickly but sometimes an example is worth thousands of lines of documentation.

So try Sourcegraph out and if you have any feedback for them, write to <a href="mailto:hi@sourcegraph.com">hi@sourcegraph.com</a>.

Sourcegraph is working really hard to make it the best tool for open source programmers.