Go言語入門 パッケージの作成、便利ツール、interfaceとは。


## 概要

「ツイートのIDがわかっていてツイートのテキストを取得したい」という状況があり、
`Twitter API`では制限が `180GET/15minutes` で遅かったので、
URLからHTMLをとってきてパースする単純な`Go`コードを書いたのですが、
リポジトリを作ってパッケージ化してしまおうかなと思った次第です。

パッケージの作成を通して自分が使っている便利ツールを紹介したいと思います。
また、コメントで他の開発便利ツール教えていただけたら嬉しいです。

### Go言語について

`Go`は開発支援ツールが充実している気がします。気がします。

あとは、ここでは触れませんが`channel`を使うと並行処理が割と簡単にできます。
（例えば、５００個のツイートを取得するために、１００個のループ５つを並列に回したり。）
あと、入門系記事にあんまり書いてありませんが、便利なのが構造体のタグです。
（jsonのキー名決めたり、ORMのカラムの型決めたりする時。`reflect`で取れる。）

## パッケージ作成

### 大まかな仕様

* APIでデータを取得する方法は考慮しない
* 取得したいツイートのURLは既知
* エラーを拡張して何によるエラーなのか分類
* HTML内に含まれる（一連の会話)リプライも取得

### 手順

1. リポジトリ作成
2. ファイル作成
3. テストコード
4. ソースコード
5. ドキュメント


## リポジトリ作成

リポジトリを作ります。
[GoDoc](https://godoc.org/?q=simpletwitter)、[Go Search](http://go-search.org/search?q=simpletwitter)、[GitHub](https://github.com/search?utf8=%E2%9C%93&q=simpletwitter)で検索してみます。

[simpletwitter](https://github.com/sosuke-k/simpletwitter)なるものを`GitHub`で作りました。（`.gitignore`とかは`GitHub`に`Go`指定して自動生成してもらいました。）
`GitHub`とかじゃないプライベートリポジトリの場合、URL的に色々障壁があるみたいです。

最初はまだ何もソースがないので `-d` オプションつけて `get` してください。

```
go get -d github.com/sosuke-k/simpletwitter
```

## ファイル作成

ファイル名はパッケージフォルダ直下にあれば何でもいいですがとりあえず `tweet.go`を作成しました。パッケーシ名は`simpletwitter`にしました。

### 【便利ツールその１】goimports

[goimports - The Go Programming Language](http://golang-jp.org/pkg/code.google.com/p/go.tools/cmd/goimports/)

![https://gyazo.com/ce65179cadd9a58efc07f3f64a364026](https://i.gyazo.com/ce65179cadd9a58efc07f3f64a364026.gif)

`Go`の書式を修正してくれるツールに`gofmt`がありますが、`goimports` は更に使われているパッケージの`import`まで自動で付加してソートしてくれます。（使われてないものが消されてしまいます。）
命名規則にあってないものや、パッケージ外から使える関数（大文字から始まる）などを定義した時に説明コメントがないものなど、色々警告してくれます。

```
go get code.google.com/p/go.tools/cmd/goimports
```

で使えます。

※ [atom](https://atom.io/)ユーザは[go-plus](https://atom.io/packages/go-plus)を入れることで保存時に実行してくれます。

## テストコード

[testing - The Go Programming Language](https://golang.org/pkg/testing/)

> create a file whose name ends _test.go that contains the TestXxx functions as described here

ので、`tweet_test.go`を作りました。

### assert

[こちら](http://qiita.com/Jxck_/items/8717a5982547cfa54ebc)で言われていますが、

> Go には標準 assert がない

思想なのでしょうが、、、自分は特にこだわりません。自分の置かれている環境で便利な方がいいです汗

### 【便利ツールその２】goconvey

[GoConvey - Go testing in the browser](http://goconvey.co/)

> it's more expressive than t.Errorf()

はい :hand:  そっちがいいです。[イントロビデオ](https://www.youtube.com/watch?v=wlUKRxWEELU)があるので、最初の方だけでも見ると雰囲気わかります。

```
go get github.com/smartystreets/goconvey
cd $GOPATH/github.com/sosuke-k/simpletwitter
goconvey
```

`goconvey`コマンドを実行するとブラウザが開きます。

![https://gyazo.com/011f0058cd178f8f0c057bb19bbf4c64](https://i.gyazo.com/011f0058cd178f8f0c057bb19bbf4c64.gif)

テーマを変更したり、デバグ出力の切り替えや監視フォルダを指定したりできます。
最初は`goconvey`を実行したディレクトリが監視フォルダになっています。

上書き保存すると自動でテストを実行し結果をブラウザ上に表示してくれるので、シンプルなエディターを使っていてマルチディスプレイとかだとめちゃくちゃ捗りますし、かっこいいです。

#### Composer

```go:tweet.go
func NewTweet(url string) (tweet Tweet, err error) {
	return
}
```

上のような関数のテストを書きたい時に、下図左のようなテキストを入力すると下図右のような雛形を出力してくれます。

![https://gyazo.com/61bd2be596e247ac701ca40c9f3aade1](https://i.gyazo.com/61bd2be596e247ac701ca40c9f3aade1.png)

`Composer`は正直、あまり使わないです。。。

#### Conveyテストコード

基本的に`Convey`関数の３つ目の引数に無名関数でテスト書きます。

先ほどの雛形を`tweet_test.go`に貼り付けると以下のような出力に変わります。

![https://gyazo.com/fca1c3eb64a61c97f1a04f075046676f](https://i.gyazo.com/fca1c3eb64a61c97f1a04f075046676f.png)

以下のようなテストにすると

```go:tweet_test.go
func TestNewTweet(t *testing.T) {
	Convey("Given available first post tweet url\n", t, func() {
		url := "https://twitter.com/olha_drm/status/418033807850496002"
		tweet, err := simpletwitter.NewTweet(url)

		So(err, ShouldEqual, nil)
		So(tweet.Text, ShouldEqual, "よるほ　あけましておめでとうございますほー")
		So(tweet.ScreenName, ShouldEqual, "olha_drm")
		So(tweet.Name, ShouldEqual, "織羽")
		So(len(tweet.After), ShouldEqual, 1)
		So(tweet.After[0].Text, ShouldEqual, "@olha_drm あけおめっ！ことよろー！！")
		So(tweet.After[0].ScreenName, ShouldEqual, "reprohonmono")
	})
}
```

こんな感じ。

![https://gyazo.com/a3f73c88d9ac0d6250f608f70067b016](https://i.gyazo.com/a3f73c88d9ac0d6250f608f70067b016.png)

### 【便利ツールその３】go-spew

[davecgh/go-spew](https://github.com/davecgh/go-spew)

デバグとかで使えるpretty print系です。

```
spew.Dump(tweet)
```

をテストコードに追加すると

![https://gyazo.com/e8b85380d43b957d0384874ea954ce2b](https://i.gyazo.com/e8b85380d43b957d0384874ea954ce2b.png)

こんな感じです。


## ソースコード

補完系だと[gocode](https://github.com/nsf/gocode)があります、、が、あまり良さがよくわかっていないので特に推しません。

※ [atom](https://atom.io/)ユーザは[autocomplete-go](https://atom.io/packages/autocomplete-go)を入れることで使えます。

### 【interfaceとは】エラーの拡張

エラーの拡張を通して `interface` をなんとなく知ります。

まず`error`の定義です。

```
type error interface {
    Error() string
}
```

つまり、文字列を返す`Error()`とう関数を持っていればいいわけです。

ここでは

* どんなオペレーションを行っていた時のエラーなのか
* どんなURLにアクセスしようとしたのか
* どんなURLにリダイレクトされたのか

といった情報を持ち、文字列を返す`Error()`とう関数を持っている`Error`という構造体を定義します。

```
type Error struct {
	Op            string // the failing Operation (Request, Redirected, Parse)
	GetURL        string // the getting url
	RedirectedURL string // the definitive url
	Err           error  // the reason the get failed
}

func (e *Error) Error() string {
	return "please input error log"
}
```

ソースコード内では

```
doc, err = goquery.NewDocument(url)
if err != nil {
	err = &Error{
		Op:     "Request",
		GetURL: url,
		Err:    err,
	}
	return
}

if url != doc.Url.String() {
	err = &Error{
		Op:            "Redirected",
		GetURL:        url,
		RedirectedURL: doc.Url.String(),
	}
	return
}
```

のように拡張したエラーを返します。

パッケージを使用する側では

```
Convey("Given non-available tweet url\n", t, func() {
	url := "https://twitter.com/statuses/418033823511629836"
	_, err := simpletwitter.NewTweet(url)

	So(err, ShouldNotEqual, nil)
	So(err.(*simpletwitter.Error).Op, ShouldEqual, "Redirected")
})
```

のように型アサーションして使います。

また、そのまま、他パッケージの関数の`err`をそのまま使いたいのであれば

```
func (e *Error) Error() string {
	return e.Err.Error()
}
```

とすればいいわけです。

このように、後でログ分析しやすいように `Error()`関数の出力を調整することができます。

なんとなく`interface`がわかった気がしないでもないです。。。

## ドキュメント

### 【便利ツールその４】godoc

[godoc - GoDoc](https://godoc.org/golang.org/x/tools/cmd/godoc)

```
go get golang.org/x/tools/cmd/godoc
```

で入ります。

```
godoc -http=:6060
```

して、http://localhost:6060 にアクセスしてみます。

![https://gyazo.com/2aa8cf7057cae54050e4d9efbc93dc95](https://i.gyazo.com/2aa8cf7057cae54050e4d9efbc93dc95.png)

`$GOPATH`下にあるパッケージが見れます。

今回の場合は http://localhost:6060/pkg/github.com/sosuke-k/simpletwitter/ を見てます。

![https://gyazo.com/19761d35c5e95d976608f4ec3b1d0c2e](https://i.gyazo.com/19761d35c5e95d976608f4ec3b1d0c2e.gif)

まだ何も書いてないのにある程度中身がわかります。

`package xxx`の上や関数や構造体の宣言の上にコメントを書くことで `godoc`で見た時に反映されるので、書いていきます。

例えば、`simpletwitter.go` を作り

```go:simpletwitter.go
/*
Package simpletwitter uses goquery(https://github.com/PuerkitoBio/goquery) to fetch tweets.

Example:

  tweet err := simpletwitter.NewTweet(url)
  if err != nil {
    if err.(*simpletwitter.Error).Op = "Redirected" {
      ...
    }
  }
  fmt.Println(tweet.ScreenName + " : " + tweet.Text)

*/
package simpletwitter
```

とすると

![https://gyazo.com/e09dbf43883f2f88e3b3e4d6bfb9c020](https://i.gyazo.com/e09dbf43883f2f88e3b3e4d6bfb9c020.png)

のような`Overview`ドキュメントが作られます。

コメント内でインデントすることで、その行がコードシンタックスになるみたいです。

## 終わりに

長くなりました。

ドキュメントがしっかりしてないライブラリは使う気にならないですよね。

かと言って、作った側は十分使えるわけで、めんどくさいですよね。

オープンソース系にコミットしている人たちはほんとに素晴らしいと思います。