motemem/gore を使う。補助として
も使うと良い
motemen/ghq を使う (すでに使ってた)。
peco/peco を使う。
Bashの設定だと
function ghql() {
local selected_file=$(ghq list --full-path | peco --query "$LBUFFER")
if [ -n "$selected_file" ]; then
if [ -t 1 ]; then
echo ${selected_file}
cd ${selected_file}
pwd
fi
fi
}
bind -x '"\201": ghql'
bind '"\C-g":"\201\C-m"'
あたりを入れる。
この章は Goland 使っているので、Goland の公式ドキュメントを読むようにする。
Tour of Go と 公式ドキュメントをまず当たるように書いている。
この辺、 Perl でいうところの Module::Starter
みたいなのがあると嬉しいんだけど、 Songmu/godzil のようなオーサリングツールを現在模索中。
error
をちゃんと使う。1.14でtry
の導入見送りになったっぽいので多値返却でいく- 正規表現はできるだけ使わず、使っても
regexp.MustCompile
で確定させておく - map を避ける。できるだけ構造体を使って型を定義する
- reflect を避ける。できるだけ型をつける
- 巨大な struct を作らず継承させようとしない。これは API の JSON Response を分割して作る時に思う
- 並行処理を使いすぎない
- Go のコードを読もう
- Go のバージョンは古いけど「GoのためのGo」も参考になるかも
- テストとCI。
go vet
やgolint
などでのチェックを入れる - ビルドとデプロイ。ビルド時の埋め込みやフラグ分岐など
- モニタリング
私がWindows 持ってないので、この章の話ちゃんと検証できてないところがある。
- ランタイムが不要なので配布しやすいが、その分クロスコンパイルに考慮する必要がある
- cgoやOS間の差異を吸収しない記述などがあるので、その点に気をつける必要がある
- OS内のファイルパス (
path/filepath
)とURLのパス(path
)の違いに注意- 間違えると場合によってディレクトリトラバーサルなどを起こす場合がある
- パスセパレーターがWindowsとmacOS/Linuxで異なるのでそこに注意
- Windowsのファイル操作に関するタイミングが異なって
defer
のタイミングを厳密にした方がいい- この話、
ioutil/TempFile
でツイート見た記憶がある
- この話、
- 基本的に内部処理はUTF-8を使う
- これは昔BSD(EUC-JP)とLinux(UTF-8)環境が混在してたときに確かにやった
Windows OS持ってないので確認できず
- OSの分岐を見る環境変数や
runtime
パッケージ - ファイル名のサフィックスに対応しているOSやアーキテクチャ名をつけておくと、
go build
時に対応した環境ファイルでビルドされる - ファイル中のコメントでも
// +build
とすることで、対応を分岐することもできる pkg-config
を使うオプションもある
- デーモナイズの話
- Perlをdaemon toolsで起動してた時代を思い出す
- Windowsの場合は nssm を使うらしい
- 静的ファイルをバイナリに含める方法。以下が紹介されている
Windows持ってないので試せてません。
- JSONかYAMLで作る
- XDG Base Directory Specificationによる規定で
$HOME/.config/(アプリケーション名)
に配置する - Go 1.12からは
os.UserHomeDir()
でホームディレクトリがcgoの状態に関わらず取れる - が、Windowsの設定ファイルは
%APPDATA%
配下にあったほうがよいので、実行環境と環境変数を
OSS化できたらいいなぁ
実用的なアプリケーションの話
- どのような機能を持っているかが容易に調べられる
- パフォーマンスが良い
- 多様な入出力を扱える
- 人間にとって扱いやすい形式で入出力できる
- 想定外の場合に安全に処理を停止できる
仕事で時間をかけて作らないと対応しづらいのと、個人のユースケースだけだと不測の事態があまり出づらいのがなぁ。
バージョンの埋め込み。-ldflag
と -X
で該当のパッケージ変数をビルド時に変更できる仕組みで変更可能。
- バッファリングの話。比較的低級な言語でないと使う機会がないのでつい忘れがち
- 精度が高い乱数が欲しいなら
crypto/rand
を使う(サンプルコード略)
- dustin/go-humanize の紹介 (サンプルコード略)
- byte変換や時間、順序数、3桁カンマなど
os/exec
を使うが、Output()
,CombineOutput()
を使う標準の方法だと以下制約がある- 出力がコマンドが実行した後に一度にまとめてメモリ上に返される
- コマンドに対して標準入力を与えることができない
- 入出力ブロックしないように処理を進めていくには標準入出力と標準エラー出力でそれぞれ goroutine を動かして、それぞれがブロックしないようにするなど考慮する
- 外部コマンド文字列を入力としてとる場合のバリデーション用のパーザとして
mattn/go-shellwords
がある
context
パッケージを使う
この辺あまりよく理解できていない。いくつかサンプル書いてテスト
- channelを使用する方法。一度closeしたチャンネルをサイド使おうとするとpanicが起こるので注意
- contextを使用する方法。selectして確認する
- チャンネルで受け取る
os.Signal
インターフェースを持つものを作れば独自のシグナル定義もできる
- 配布のしやすさ
- 複数プラットフォームへの対応のしやすさ
- パフォーマンス
ランタイムなしにワンバイナリで配布ができるのは作者として楽ではあるのだけど、ライブラリを使っていると知らないうちに cgo 周りで Windows 動かない場合があって難しいところがある。
- CLIツールのインターフェース
- シングルコマンドパターン:標準の
flag
パッケージを使う - サブコマンドパターン:サードパーティーのものを使う。詳しくは 4.4
- シングルコマンドパターン:標準の
- リポジトリ構成
- バイナリをメインにするのか、ライブラリをメインにするのか
- メインにする方が第一階層
- ライブラリがメインで、コマンドがサブの場合は
PROJECT_ROOT/cmd/COMMAND_NAME/main.go
として配置
- ライブラリがメインで、コマンドがサブの場合は
- 標準パッケージの
flag
パッケージの紹介 flag.Type
(ポインタ渡し) またはflag.TypeVal
(値渡し) をした後にflag.Parse()
で値を取得- ショートオプションとロングオプションを設定したい場合は両方とも記述が必要
- フラグの記載場所。著者は非パッケージスコープでの定義を推奨
flag
の設定を変えることで出力先を変更するなどが可能なので、テストのときに利用する- コマンドライン引数をパースするような型をカスタムで作成することも可能(カンマ区切り値が例に挙げられている)
- サードパーティーのフラグのパッケージとして以下が例示されている
- サードパッケージ製のパッケージ紹介
- 著者の使っている mitchellh/cli を詳しく説明
- サブコマンドをインターフェースとして定義
- 使いやすいツール
- ステータスコードを返却する。
os.Exit(statusCode int)
を使う log.Fatal
など、defer
の期待通りの挙動にならないものがある
- ステータスコードを返却する。
- 標準出力と標準エラー出力
io.Writer
を引数に取るfmt
などの関数で出力先を変更できる
- エラーは
error
を介してコンテキストを付与する - メンテナンスしやすいツール
- CLIのテスト
- ステータスコードの確認
- メッセージの出力
*bytes.Buffer
に書き出すことでテストしやすくする
- CLIのテスト
- 型の検出と型アサーションの限界
- 型アサーションだと、想定しうる型を全て列記しなければならない
- これに操作するには
reflect
を使う
reflect
パッケージを使って型を判定し、型にあった対応をする- 異なった型に対する処理を実行すると
panic
が起こる reflect.StructTag
でstruct
のタグを判定できる。- JSONなんかのタグはこんな感じでパースされてるのか
- 値を動的に生成したり、スコープの話だったり
- 「ポインタとinterfaceの値」の「Setできる値」のところ、前節でpanic起こしてたコードだ
- 動的に
select
を構築することができる
- reflectは実行時に逐次確認をするので、パフォーマンスが悪くなる
- 本当に必要なときにしか使わない方がよい
testing
パッケージの使い方
- テストファイルにおける命名規則やマジックコメントについて
go test -bench
の使い方と見方
- Table Driven Test
- 入力と期待値を持つ構造体の配列のテーブルをループしてテストを実行する
- 構造体の比較は
reflect.DeepEqual
で行う -race
フラグでRace Detector を使うTestMain
を使うことでxUnit
のstartUp
/shutDown
のような機能を使う- マジックコメントでタグをつけることができる。テストの種類を分けるのにも使える
- テストにおける変数または手続きの置き換え (パッケージ変数の置き換え)
- インターフェースのモッキング
net/http/httptest
パッケージ- テストカバレッジは
-coverprofile
や-covermode
を使う
database/sql
の働き
- lib/pq を使ってデータベースに接続
- プレースホルダーの話
- 実行結果の話
- 行イテレーターが帰ってくるので、ループしながら割り当て
- データベースカラムの型のGoの型へのマッピングの話
- 次の節で go-gorp/gorp を使うための説明
- Webフレームワークとして labstack/echo を使う
- Validatorとして go-playground/validator.v9 を使う