Golang に関する投稿を表示しています

Go言語のスイッチが一瞬わからなくなったのメモ

一瞬わからなくなったのでメモ。
https://play.golang.org/p/AmpjLMDFPf8

そもそも型スイッチは interface{} は値を任意の型になっているか確認して、その型にキャストされた状態にしてくれるすごいやつ。
A Tour of Go に出てくるので知っている人も多いハズ。
Type switches

Go なりの型の持ち方、ようはダックタイピングをイマイチちゃんと使ってなかったのもあって理解ができてなかったが
"値がこういうものを持っていたらこの型だよね" という話なだけと、とりあえずのところは理解した

ある値が String() な関数を持っている interface{} ならそれは fmt.Stringer() を満たしているのでキャストができるし
他にも String() だけを要求する interface があれば、それを満たしているともいえるので、その型キャストが出来る。

 

あと混乱をきたすので深掘りせずにイイ感じに応対したいんだけど、そこにポインタが絡んでくると謎っぽい。

https://play.golang.org/p/MHIX7i1Royk

まあ、特に理由がなければ struct の扱いはポインタでよいんじゃないだろうか。
CodeReviewComments にも怪しかったらポインタレシーバを使うのだって書いてあった。
CodeReviewComments · golang/go Wiki · GitHub

 

話がそれてきたのでおわり。

zapcore.Field を生成する関数を自分で定義する

uber-go/zap: Blazing fast structured leveled logging in Go.

zap を使ってログを出すときに自分でフィールドを追加できる。

こういう雰囲気で。

logger.Info(
    "invalid address",
    zap.String("address" address)
)

このとき zap.String って書いているけど、これを自分で定義できるのかな?と zap のソースコードを読んだりしてたらできそうなことがわかった。
でもって試したのが、これ。

sters/zap-hashed-field: Define hashed zapcore.Field

具体的なユースケースがわからんけど、例えば入力されたデータをもとに動きを追いかけたい、とか、なにがしかの都合で、センシティブなデータ(パスワード、メールや電話番号、トークン値、…etc)を出したいときがある、と思う。そういうときに使えるんじゃね?的な妄想をしながら作った。

こういう風に使ってハッシュ値にして出力できる。

logger.Info(
    "invalid address",
    hashedfield.Sha1("address" address)
)

 

これをどうやってやるかっていうと、単に zapcore.Field が構造体になっているので、それを生成して zap.Logger で出力する場所に渡してあげるだけでよかった。

zapcore.Field は以下のようなフィールドを持っている。
zap/field.go at master · uber-go/zap

type Field struct {
	Key       string
	Type      FieldType
	Integer   int64
	String    string
	Interface interface{}
}

このとき Type に指定したものに応じて zap.Logger がよろしく値を変換して出力してくれる。文字列を出したいなら StringType を指定して String フィールドに値を入れればよい。
zap.String がまさにそう。
zap/field.go at master · uber-go/zap

func String(key string val string) Field {
	return Field{Key: key Type: zapcore.StringType String: val}
}

 

使い方のサンプルってどうやって書いたらいいのかなあと思いながら zap のコードを眺めていたら
examples_test.go というファイルがいて、なんだろうな、と。

zap/example_test.go at master · uber-go/zap

そこから調べてたら Examples Test を知った。

Testable Examples in Go - The Go Blog

要するに、テストコードの最後に期待する出力をコメントで書いておくと出力を内部でハンドリング、比較して様子を見てくれるとのこと。
便利~~~~。

map の構造をチェックした

テストを書くとき、値はどうでも良いが、キーだけちゃんとチェックしたい。要はmapなんだけどものによって期待する構造が微妙に違うんじゃ????ってときがあったのでやってみる。
ふわっとした形をがんばって扱うのGo言語っぽくない気がするーーー、うーーーーーーんんんん。。

package hoge_test

import (
	"testing"
	"sort"
	"reflect"
)

func checkMapStructure(t *testing.T, expectMap map[string]interface{}, actualMap map[string]interface{}) {
	var expectKeys []string
	var actualKeys []string

	for expectKey, expectVal := range expectMap {
		expectKeys = append(expectKeys, expectKey)

		if expectChildMap, ok := expectVal.(map[string]interface{}); ok {
			actualVal, ok := actualMap[expectKey]
			if !ok {
				t.Fatalf("wants actualMap[%+v], but not found", expectKey)
			}

			actualChildMap, ok := actualVal.(map[string]interface{})
			if !ok {
				t.Fatalf("wants actualMap[%+v] is map, but not map", expectKey)
			}

			checkMapStructure(t, expectChildMap, actualChildMap)
		}
	}

	for actualKey := range actualMap {
		actualKeys = append(actualKeys, actualKey)
	}

	sort.Strings(expectKeys)
	sort.Strings(actualKeys)

	if !reflect.DeepEqual(expectKeys, actualKeys) {
		t.Fatalf("expectKeys = %s, actualKeys = %s", expectKeys, actualKeys)
	}
}

func TestHoge(t *testing.T) {
	x := map[string]interface{}{}
	ex := map[string]interface{}{}

	x["1"] = 111
	x["huga"] = map[string]interface{}{
		"a": 1,
		"b": 1,
		"c": 1,
	}

	ex["1"] = "hoge"
	ex["huga"] = map[string]interface{}{
		"a": "1234",
		"b": "1234",
		"c": "1234",
	}

	checkMapStructure(t, ex, x)

	t.Fail()
}

とりあえず string なキーだけやっているけれど reflect.DeepEqual で検証しているので string じゃなくてもなんでもできると思う。
逆に string 以外のキーを持った map って需要あるんかな、わからん。

PHP の $_REQUEST を Golang でもやりたい

いや要らないだろ…と思いつつもやる必要がうっすらとでてきたのでやってみた。

ようは、クエリパラメータと POST の中身とクッキーから読み取れれば良い。

PHP: $_REQUEST - Manual

やや雑ではあるがお試しするとこんな感じになると思う。

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"time"
)

func PHP_REQUEST(r *http.Request, key string) string {
	// Get parameter
	if val, ok := r.URL.Query()[key]; ok {
		return val[0]
	}

	// POST
	tmp := r.PostFormValue(key)
	if tmp != "" {
		return tmp
	}

	// Cookie
	for _, cookie := range r.Cookies() {
		if cookie.Name == key {
			return cookie.Value
		}
	}

	return ""
}

func main() {
	go func() {
		http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("hoge = " + PHP_REQUEST(r, "hoge")))
		})
		http.ListenAndServe("0.0.0.0:8000", nil)
	}()

	time.Sleep(time.Millisecond)

	var client *http.Client
	var r *http.Response
	var b []byte

	// nothing
	client = &http.Client{}
	r, _ = client.Get("http://0.0.0.0:8000/test")
	b, _ = ioutil.ReadAll(r.Body)
	fmt.Println(string(b))

	// get
	client = &http.Client{}
	r, _ = client.Get("http://0.0.0.0:8000/test?hoge=get")
	b, _ = ioutil.ReadAll(r.Body)
	fmt.Println(string(b))

	// get / cookie
	client = &http.Client{}
	u, _ := url.Parse("http://0.0.0.0:8000/test")
	client.Jar, _ = cookiejar.New(nil)
	client.Jar.SetCookies(u, []*http.Cookie{
		{
			Name:  "hoge",
			Value: "cookie",
		},
	})
	r, _ = client.Get("http://0.0.0.0:8000/test")
	b, _ = ioutil.ReadAll(r.Body)
	fmt.Println(string(b))

	// post
	client = &http.Client{}
	v := url.Values{}
	v.Add("hoge", "post")
	r, _ = client.PostForm("http://0.0.0.0:8000/test", v)
	b, _ = ioutil.ReadAll(r.Body)
	fmt.Println(string(b))

	time.Sleep(time.Millisecond)
}

mapから指定したキーを検索するやつ

map[string]interface{} が入れ子になったものを扱うときがあって
たぶん、こういうなんだかわからないけど、いい感じに区分けされたデータ列があったときに使うんじゃないかな、きっと。
(前段階のアプローチがそもそも悪いかも)

{
  "case-1": {
    "a" : {
      "id" : 1
    },
    "b" : {
      "id" : 2
    }
  },
  "case-2": {
    "c" : {
      "id" : 3
    }
  }
}

その中から特定のキーをいい感じに取り出したいなあってなったので作ってみた。
GitHub - sters/maputils

 

余談になるけど
どこかで再帰呼び出しとループだと、ループのほうが速いよって書いてあったので、再帰的にキーを探すところはループでやってみた。

それでもって、ベンチも取ってみた。
gist:521e2be8b3aeed0c87fe75f7335f259e

謎な map の作り方してるけど、これくらいの規模感でやらないと、手元だとまったくもって差がでなかった。
逆にいえば、これくらいの規模感を取り扱う or これくらい負荷がかかるようになって初めて効果あるくらいなのかあという気持ち。


書いてて思ったけど []struct から特定のフィールドだけ取り出すほうが需要高そうだしもうありそう。

GoLand でカバレッジを表示した

結論 Java のそれなどと同じようにやれば良い

スクリーンショットが多くてわかりやすかった Q&A
Intellij Idea : view test coverage on a maven project - Stack Overflow

 

Cmd + Shift + A したときのコマンドパレットに Show Code Coverage Data というものがあり、これを開くとあたかもファイルを追加できそうにも見えるが go test -cover したときの結果を使うことはできないっぽい。

JetBrains 系エディタを通してカバレッジ計測した結果を表示するよ、というものみたい。

Go 言語の練習に markdown のテーブルだけいい感じに調整するやつを作ってみた

作ったよ

sters/markdown-table-formatter: markdown-table-formatter

|hoge            |huga|
|--------------|------|
|its|confusing              |markdown|
|table   |so|crazy|table|.|

みたいなガタガタテーブルが書かれているテキストをキレイに揃えてくれるやつ。

|hoge |huga     |        |     | |
|-----|---------|--------|-----|-|
|its  |confusing|markdown|     | |
|table|so       |crazy   |table|.|

 

何も考えずにこんな要素が必要では?とテストを書いていって、それに合わせて実装をしていったので、同じことを何度もやっているような部分もあって、
アーキテクチャ的には再考の余地あり。

struct に中間データ的なものを閉じ込めてバケツリレーするのが簡単かなあ。
テストがいるので、ここからリファクタしがいありますね、って感じで。

 

書いてみて、ポインタとかまったく気にしなくて良いってことがわかってきた。
出番はほぼないのと、あっても波線出たときにどうにかする、ということだけを、とりあえず、それでよさそう。

リリースの配布

タグを設定すると Circle CI が動いてリリースが作られるようにしてみた。
(go get に必要なのかな?と思ったけどそういうわけではなかったみたい)

goreleaser を使ったリリースというタスクを作って、ワークフローで制御している。

goreleaser/goreleaser: Deliver Go binaries as fast and easily as possible

設定はここみて。

markdown-table-formatter/config.yml at master · sters/markdown-table-formatter

詰まったポイント

テスト時の assert を見やすくするために gopwt を使ってみたんですが、-cover したときにカバレッジが出なくなる問題があり、そこで無駄に詰まった。
gopwt のソースを調査するのはまたの機会にして、カバレッジ測定では gopwt を無効にした。

markdown-table-formatter/Makefile at master · sters/markdown-table-formatter

あと go get したときにそのままバイナリができる方法がわからなかった。ググってもそれっぽい回答が見つからない。。公式ドキュメントのどこかにいるのかなあ…

わからなかったので、リリースを作ってみたが違った ./main.go にして見たらバイナリが作られるようになった。
ディレクトリ切るとだめなんかな。いや、./formatter.go がいたからダメだったのかな、、
ドキュメントどこーー

そのほか

カバレッジ出ると楽しい。
100%にするのが完璧なテストというわけではないけど、そもそもココのテストできてなくね?とかが可視化されるの便利。

Go 言語に入門する

やるかやるか〜〜〜って思ってたけど、やっとやった。

A Tour of Go をやってみる

Go 言語の入門するにはとりあえず A Tour of Go をやると良いって噂を聞いたのでやってみる。

A Tour of Go

ブラウザ上で Go 言語のプログラムを書いて、そのまま実行できるスゴイやつ。
課題も途中で出てくるので、実際に動かしながら各種の理解を進められる。

とりあえずやってみての感想は interface と struct そして ポインタが絡むとよくわからんとなった。
もっとコード書いたらわかってくると思うので一旦雰囲気を理解するだけで。

あと標準パッケージがワカラン。このへんもやっていくしかない。

ただなんとなく、超便利有能関数群みたいなのが無いあたり、俺達のプログラムに必要なコードは俺達で組むんだ、みたいな意思を勝手に感じた。
パッケージは Github から直に入るのでどういうのがよく使われているのかまったくわかんない… awesome-go を見るのがとりあえずは良いかもしれない。

avelino/awesome-go: A curated list of awesome Go frameworks libraries and software

Go 言語のランタイムを入れる

Downloads - The Go Programming Language

Mac なら brew install go で入る。

GOPATH は設定してもしなくても良くて、設定しないと ~/go/ になる。特に移動する必要も感じなかったのでそのまま。
GOROOT は設定必要だよ!とか書いてある記事もあるが、現在に置いては不要らしい。

ghq をいれる

motemen/ghq: Remote repository management made easy

~/.gitconfig に ghq の設定を追記する。


[ghq]
root = $GOPATH/src/

ざっくり説明すると、リポジトリの管理をお手軽簡単にやるツール。

Go 言語のリポジトリをこねくりまわすとき GOPATH/src 以下で作業をする必要があるので、これを使って GOPATH/src に配置されるようにしておくと困らなくて良い。
(git clone して作業しようとしたら GOPATH/src におくんやで!みたいなエラーが出てちょっと困った)

Visual Studio Code を入れる

Visual Studio Code - Code Editing. Redefined

エディタ atom でも良かったんだけど、せっかくなので別のやつを試してみる。
個人でポチポチやってみるのに Gogland ほどのウルトラハイパフォーマンス IDE は一旦いらないんじゃなかろうかという気持ち。

拡張機能はとりあえずこの2つを。

  • Go
    • Go 言語サポートを追加する
  • Dracula Official
    • おすすめテーマ

Go を入れるだけで、裏で go ほげほげなコマンドを使った定義元ジャンプや整形や lint やデバッグまで色々多数用意される。
初めて Go を入れたときに必要な Go パッケージが go get されているかを自動で確認してくれて、無い場合はよろしくやってくれる。

GOPATH 以下が見れる GOLANG というタブ?がエクスプローラーの中に出ているので、ここからこのまま作業をしてもいいし、ディレクトリを開いて置いても良いと思う。

ディレクトリを開いたほうがターミナル起動時に、カレントディレクトリに移動してからやってくれるので便利。
GOPATH のもので見ると GOPATH を最初に開くので移動する手間あり。

----

とりあえずはこんなとこで。