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

Withなんちゃらなオプション( Functional Option Pattern )

Go 文化なのかわからないけど New とか構造体作れる関数や初期化する関数へのオプションを設定するためのコールバック関数を設定するキメ文句をよく見るような気がする。

func WithInsecure() func(*Config) {
    return func(cfg *Config) {
        cfg.insecure = true
    }
}

func WithUserAgent(ua String) func(*Config) {
    return func(cfg *Config) {
        cfg.userAgent = ua
    }
}

func main() {
    req := NewRequest(
        WithInsecure(),
        WithUserAgent(),
    )
    req.Send()
}

こういう感じのやつ。

オプションを外のパッケージに対して隠せるっていうメリットがあるのかな。
流れに乗っておくと、雰囲気で利用できて便利そうだなーって思った。


追記:2019年3月4日
これは Functional Option Pattern だ、という話をまったく違う流れで社の Slack で知った。

Functional Option Pattern

Config 構造体でがんばって持つのもいいけど、文量増えてきたり、外に公開するような API であった場合にちょっと不便かなあと思っていたので、このデザインはいいなと思った。

envconfig の

kelseyhightower/envconfig: Golang library for managing configuration data from environment variables

ざっくり説明

環境変数を読み込んで構造体におとせるやつ。
構造体のタグに環境変数名をいれると、任意の環境変数をパースしたときに展開してくれる。
書かなくてもよしなに動く(すごい)

構造体は入れ子にできる

タグに必須有無やデフォルト値をかける。

使い方

リードミーに書いてあるので読むべし。
envconfig/README.md at master · kelseyhightower/envconfig

よしなにしてくれるが、毎回タグを書くようにしたほうが事故防止や分かりやすさからもいいと思う。

type (
    // Config of the app.
    Config struct {
        // Service config
        Environment string `envconfig:"ENVIRONMENT" required:"true"`
        LogLevel    string `envconfig:"LOG_LEVEL" default:"ERROR"`
        Version     string `envconfig:"VERSION"`
    }
)

なぜ使うか

ツール類なら flag パッケージで十分だと思う。
アプリケーションとしてだと環境変数に値をいれておいて設定を変えたい場合があるのでは。
DB の接続先とか。

Laravel のときにお世話になってた dotenv もそういう感じよね。
Configuration - Laravel - The PHP Framework For Web Artisans

Go でも dotenv が出来るやつある。
joho/godotenv: A Go port of Ruby's dotenv library (Loads environment variables from .env.)

これは構造体にシュッとならないので map 経由してよしなにやる必要ありそう。
envconfig は構造体にシュッとなって便利そうではある。

どうやって実装されているのか

これは完全に興味で羽を伸ばしてみる。

このソースだけで完結できる。意外と短い?
envconfig/envconfig.go at master · kelseyhightower/envconfig

Process がエントリポイントで、すぐに gatherInfo を呼ぶ。
この中では、引数のバリデーションと、渡された構造体を reflect パッケージを使って、タグや形、キーたる名前をパースしてる。要は構造体の情報から必要なものだけ集約したもの。それが varInfo のスライスになる。
このとき、構造体のタグに split_words がついていると、ベストエフォートでキャメルケースを、スネークケースに変換してキーを設定する。

構造体を持っている場合、それがデコード出来ない形式なら再帰的にパースを試みる。
がてことは割と雑多に構造体を放り込んでもよしなに動いてくれそうではある。

このようにして、読み込んだデータを格納する構造体についての分析が終わったら、いよいよ環境変数から値を読み込む。

すでに確認されているキー名で lookupEnv(os.getenv にあたる)をして、環境変数から読み込み、なければデフォルト値を試みて required の判定をする。
読み込んだ値は文字列でしかないので、構造体の形情報に合わせて変換していく。それを processField でやっている。

このとき、対象が Decoder か Setter を実装していると、それを使って構造体のフィールドを戻そうとする。あるいは TextUnmarshar,BinaryUnmarshaler でも。

サンプルだと IP アドレスな文字列から net.Ip 構造体に落としている。
たとえばだけど暗号化した値を渡して復号化することもできし json を入れて構造体に戻すこともできるし DB 接続の情報をいれて sql.DB のクライアントを作ったり API の接続情報から http.Client を作ることもできる。
もはや設定値から作るようなものなら何でも作れる。
やりすぎるとコード読む側が苦になりそうだけど…。

話をコードに戻すと、あとはエラーの場合がある程度で、以上。

Process がエントリポイントと最初に書いたけど MustProcess というのもある。
こっちはエラーになったら panic する。

regexp.MustCompile なんかと同じ。

おわり。

dd-trace-go を使って Datadog の Span をモリモリ作る

import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"

func hoge(ctx context.Context) (err error) {
    span, ctx := tracer.StartSpanFromContext(
        ctx,
        "do hoge",
    )
    defer func() { span.Finish(tracer.WithError(err)) }()

    err = somethings1(ctx)
    if err != nil {
        return
    }

    err = somethings2(ctx)
    if err != nil {
        return
    }

    return
}

こういう具合に tracer.StartSpanFromContext してあげるとよい。
StartSpanFormContext を使うと context に実行時の span を保持してくれて、親子関係を構築してくれる。

で、これをサードパーティなパッケージが絡んだところや、その他いろいろにモリモリ適用しようとすると大変そうだなあと思ったので、クロージャでシュッと StartSpanFormContext を書いてみた。

sters/wrapspan: for https://github.com/DataDog/dd-trace-go/

別に何も難しいことはやってない。


dd-trace-go に contrib パッケージがあるように、対象のソレをラップした構造体なり関数なりを作るほうが、そういうパッケージとして切り出せるしオプションの勝手もよいだろうしコードの見通しもよくなりそうだ、と、この記事を書いているときに思った。

Readme に書いた例だとこういう感じ。

func Xsomething1(ctx context.Context) (err error) {
    span, ctx := tracer.StartSpanFromContext(
        ctx,
        "span-1",
    )
    defer func() { span.Finish(tracer.WithError(err)) }()

    return X.something1(ctx)
}

func Xsomething2(ctx context.Context) (err error) {
    span, ctx := tracer.StartSpanFromContext(
        ctx,
        "span-2",
    )
    defer func() { span.Finish(tracer.WithError(err)) }()

    return X.something2(ctx)
}

func Xsomethings(ctx) (err error) {
    span, ctx := tracer.StartSpanFromContext(
        ctx,
        "span-somethings",
    )
    defer func() { span.Finish(tracer.WithError(err)) }()

    err = X.something1(ctx)
    if err != nil {
        return
    }

    err = X.something2(ctx)
    if err != nil {
        return
    }
}

よさそう。

Windows で dep ensure したときに File name too long が出るのを解決した

どうやら Windows はパスの文字数が 260 文字という制限があるらしい。

が、それをレジストリで設定変更できる。ここを 1 にしたらよいとのこと。

\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem

LongPathsEnabled = 1

Pro やら Enterprise やらだとグループポリシーの設定が必要そう。

Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem

Enable Win32 Long Paths

see: Long File Names?


そしてさらに git の設定が必要そう。

git config --system core.longpaths true

see: Sorce Tree で"Filename too long"のエラーが出た時の対処 - Qiita

PHP を Go にするという話を mercari.go というイベントで喋りました

PHP を Go にするという話を mercari.go というイベントで喋りました(今更)

mercari.go #4 を開催しました - Mercari Engineering Blog

スライドに書いてある話がおおむねすべてです。

リポジトリはここ: sters/phptogo: Transpile PHP code to Go like something code.

今のメルカリの状況については、各所の web メディアにかかれている Mercari Tech Conf の話がいちばんまとまっていると思います。

この記事では後付けというか、いくつか補足できればと思いますー。

コードすべて変換して手直しはそう大変でもない

初期のもの(スライド中の v1)では、丸っと全コード変換はしていません。

Go でざっくりと書いたテンプレートファイルを作っておいて、そこに struct を書いていて、元の PHP コードから function 単位で、該当するテンプレートファイル上の関数へと埋めていく、といったことをやっていました。

Github で公開したものはその手続きを含んでいないので、大変そうに見えると思います。
(実際これでやれって話になったらチョットつらい)

加えて、各種 Go ヘルプするツール群を利用すれば
自動である程度整ってくれるので、マジつれーわーうわーみたいなことにはならないとおもっています。

型情報もう少しなんとかしたい

PHPDoc あるいは PHP の型宣言の情報を利用すれば、もっと埋めることができます。
そのとき、わからないやつは一旦全部 interface{}でゆるっと扱う、みたいなことになると思います。

関数においての PHPDoc からの補完はすでに書いています。
add function parameter type completion form phpdoc · sters/phptogo@b8e3d96

テストどうやる問題

確かにテストは本当に正しいのかどうかわからないというのもわかります、実際わからない。

一つのやり方として、生成物が Go のような何かのコードではなく、完全に Go の AST を作ってしまう、という手が考えられます。
テストとしては AST→AST ができることを確認すればよいので、今よりは信頼できる(?)テストになるんじゃないでしょうか。
当然そのまま持っていくことがムリな情報は落とさざるを得ないので、落とすか、それっぽい値で補完するかのどちらかになるのは今と変わらないです。

Githubで公開したものに色々追加したいねー

Github で公開したものは、セミコロンないよとか変数に$はつかないよとか、ごくごく基本的な構文の変換くらいしかやっていません。

もっと足を伸ばして PHP クラスを Go の構造体に落とすぞ!!!みたいなものはちょっとやりすぎで、そのとき、うまく出来るように一定のフォーマットであるとか、フレームワークのようなものを利用者に強いることになってくると思うのでそのフレームワークのようなものを理解しなければいけないのは微妙かなと。

ということで、必要なもの、なんとかしたいもの、はそれぞれで実装してくれ!ただ、それだと勝手が悪いところも多いと思うので、サンプルの文量を増やしていくかなあとはおもっています。
たとえば初期実装ではやっていたようなテンプレートファイルに落とし込むようなやつとか。

そもそもPHPをGoに変換する必要があるのか

いや、そんなにないんじゃないですかね……

複雑すぎないクラスやバッチがたくさんあって、何かしらの理由で Go に持っていきたい、という場合に需要がでてくると思います。
ただ PHP で今動いているものを Go で動かすことに対して、手間ひまがかかることは当然なので、効果というか色々と天秤にかけたうえでやったらいいんじゃないですかね~~~。

欲する人のそれぞれの状況によると思うので一概に答えはないと思います。
僕の状況ではだんだんつらくなってきて欲しくなったので作っていった、というだけです。

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 から特定のフィールドだけ取り出すほうが需要高そうだしもうありそう。

1 2