使用しているAlfred Workflows一覧
環境
- Alfred 4.0.3
Alfred Powepackを利用している場合は2台のMacでAlfredの設定を同期することも可能(ドキュメント)だが、1台からの片方向同期かつWorkflowsだけを同期するのは難しそうなので、業務で使用するMacとは手作業で同期を行うことにしようと思った。(JIRAチケットへのジャンプ等、業務でしか使わない設定も少なくないので...)
今後も良さそうなWorkflowsがあったら、このドキュメントに追加していく。
Workflows
(自作)フォルダを検索してターミナルで開く
- filefilter(public.folder) → Browse in Terminal
Docker Compose上で起動したwebpack-dev-serverに設定されたプロキシが動作しなくてハマった話
環境
- macOS 10.14.4
- docker-compose version 1.23.2
- webpack-dev-server 3.7.2
- webpack 4.37.0
フロントエンド(webpack-dev-server)、バックエンドをそれぞれDocker Compose上にコンテナとして起動して開発していた際にハマったお話。
事象
webpack-dev-serverはホストするjs内で発行されるHTTPリクエストをプロキシすることができる
webpack.config.js
を以下のように設定(抜粋)- APIサーバーは5000番ポートを開いているとする
devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, port: 8080, historyApiFallback: true, proxy: { '/api': 'http://localhost:5000', }
- 上記設定を反映したwebpack-dev-serverを起動し(
docker-compose up
)、実際にlocalhost:8080にアクセスしてfetch('/api')...
をブラウザから実行すると、webpack-dev-serverが以下のエラーを出力した
[HPM] Error occurred while trying to proxy request /api from localhost:8080 to http://localhost:5000 (ECONNREFUSED) (https://nodejs.org/api/errors.html#errors_common_system_errors)
- httpレスポンスは以下(抜粋)
504 Gateway Timeout Error occured while trying to proxy to: localhost:8080/api
原因
Docker Compose上のコンテナが参加するネットワークでは、
docker-compose.yml
上のサービス名で名前解決が行われるため。従って、
webpack.config.js
を下記のように書き換えると上手くいく
... proxy: { '/api': 'http://api:5000', }
- なお、
docker-compose.yml
では以下のようにサービス名をapi
としている
version: "3.4" services: api: build: context: ./api ...
敗因
- 書いてみるとこれだけのことだが、多分2,3時間ぐらいハマっていた
- サービス名で名前解決ができること自体はDocker Composeのドキュメントに書いてあることだし、以前から知っていた。なぜ気づけなかったのか...
- 多分だが、ブラウザからDocker Compose上のwebpack-dev-serverにアクセスする際は、
- のでwebpack-dev-serverが動作しているのがDocker Compose上であることを忘れていたというか、しっかり認識できていなかったような気がする。
- また、Docker Compose上のネットワーク内のコンテナ間もlocalhostで通信できるようなイメージだったのも原因かもしれない。
- あと、ステータスコード504 Gateway Timeoutの理解が不足している感もある。webpack-dev-serverがlocalhost:5000が存在しないためにレスポンスを得られない→ポート5000で動作しているはずのHTTPサーバーのログを確認する→ログがない→接続先がおかしい?というような手順をスムーズに踏めたら良かった。
- Goで書いているHTTPサーバは外部ファイルへのログ出力を未実装で、結局調査のために書いたんだけど、ここを面倒くさがらずに早く実行するべきだったかな...
bashコマンドは読取権限のあるファイルなら処理できる
環境
Permission denied
$ touch hoge.sh && ./hoge.sh
するとbash: ./hoge.sh: Permission denied
になる- 実行権限がない
- しかし
$ touch hoge.sh && bash hoge.sh
は正常終了するのはなぜか?
読取権限
httptest.Serverとhttptest.ResponseRecorderの使い分け
環境
httptest.Serverとhttptest.ResponseRecorder
- ServerとResponseRecorderは何が違うのか?どう使い分けるのか?
- 答え:When to use httptest.Server and httptest.ResponseRecorder
httptest.Server
- httptest.Serverは外部のHTTPサーバと通信を行うコードをテストするために利用する(外部APIの挙動のエミュレート)
- クライアント-サーバモデルで言うと、テストしたい対象がクライアントの場合に相当する模様
httptestパッケージより引用
package main import ( "fmt" "io/ioutil" "log" "net/http" "net/http/httptest" ) func main() { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, client") })) defer ts.Close() res, err := http.Get(ts.URL) if err != nil { log.Fatal(err) } greeting, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { log.Fatal(err) } fmt.Printf("%s", greeting) }
httptest.NewRecorder
- httptest.NewRecorderは純粋にHTTPリクエストに対するハンドラーの動作をテストするために利用する
- 例えばHello worldを返すだけのWebサーバのコードを書いた場合のテストコードはhttptest.NewRecorderで十分
httptestパッケージより引用
package main import ( "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" ) func main() { handler := func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "<html><body>Hello World!</body></html>") } req := httptest.NewRequest("GET", "http://example.com/foo", nil) w := httptest.NewRecorder() handler(w, req) resp := w.Result() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(resp.StatusCode) fmt.Println(resp.Header.Get("Content-Type")) fmt.Println(string(body)) }
関連:NewRecorderを利用する場合にserveHTTPを使用する必要があるのか
http.HandlerFunc(articlesHandler).ServeHTTP(rr, req)
してレスポンスを検証している例もある- 例えばTesting HTTP handlers in Goや他の書籍のサンプルコードにもあった
- マルチプレクサ経由でhandlerを呼び出している(HandleFuncはマルチプレクサ(ServeMux)を返し、ServeHTTPはマルチプレクサに登録されているパターンにマッチするハンドラを呼び出す)
- 結局テストコード中でハンドラを登録しているなら初めからServerを使うか、ハンドラの処理だけにフォーカスするかした方が良いのではないか?(Goのhttpパッケージをテストしたいわけではない)
参考
draw.ioで両端が矢印の線を引く方法
AWSの構成図をdraw.ioで描いていて、両端が矢印の線を引く方法が分からなかった。もう諦めてCacooを使おうかと思っていたところ解決策が見つかった。
How to create a bidirectional arrow? : draw.io Helpdesk
ちなみに上記で操作しているサイドバーはcmd+shift+p
で表示できる(Macの場合)
VSCodeでGoのデバッグ
今更感はあるがVSCodeでGoのソースコードをデバッグしてみたので記録。
環境
delveインストール
VSCodeにテスト対象の$GOPATHを認識させる
- 方法は複数あるが、VSCodeのworkspaceディレクトリを自動的に$GOPATHとして認識してくれるようにするのが1番手軽そう
- settings.jsonに
"go.inferGopath": true
を追記 - stackoverflow.com
- settings.jsonに
launch.jsonを作る
- テスト対象のプロジェクトをVSCodeで開き、[構成の追加]をクリック(画像は既に構成追加済み)
- launch.jsonが作成される(作成済みの場合は追記される)
{ // IntelliSense を使用して利用可能な属性を学べます。 // 既存の属性の説明をホバーして表示します。 // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Launch", "type": "go", "request": "launch", "mode": "auto", "program": "${fileDirname}", "env": {}, "args": [] } ] }
"program": "${fileDirname}",
は開いているファイルが属するパッケージをテスト対象にする- その他の設定値に関する説明はDebugging Go code using VS Code · microsoft/vscode-go Wiki · GitHubを参照
デバッグする
- breakpointを置く
- テストコードの[debug test]をクリックする。テストメソッド単位で実行できる。
- breakpointの位置で処理が止まる。テストメソッドの中でも止められるし、テストメソッドから呼ばれた先のメソッドの中でも止められる。
- これでテストが通らなくなった場合の原因調査がスムーズに
まとめ
- VSCodeでソースコードのdebugを行う際は、launch.json等の設定ファイルへの習熟が必要な印象があったが、Goの場合はあまり考えることがない
- delveは必要だったが、それ以外はVSCodeとGo本体というシンプルな構成でデバッグ可能
Goやっていきましょう
型アサーションでインターフェイス型から本来の型を取り出す際の演算子*
環境
インターフェイス型の型アサーション
Goでは、以下のようにインターフェイスのエイリアスであるStatement
型の変数から実際の型であるExpressionStatement
を型アサーション.(T)
によって取り出すことができる。
package main import "fmt" type Statement interface { // [...] statementNode() } type ExpressionStatement struct { // [...] hoge string } func (es *ExpressionStatement) statementNode() { // [...] } func getExpressionStatement() Statement { return &ExpressionStatement{hoge: "hoge"} } func main() { exStmt := getExpressionStatement() stmt, ok := exStmt.(*ExpressionStatement) fmt.Println(stmt.hoge) fmt.Println(ok) }
演算子*
の意味
ところで、実際に型アサーションを行っているstmt, ok := exStmt.(*ExpressionStatement)
の*
はどういう意味なのか。
最初は、式の中なのでポインタ型変数の中身を参照(dereference)しているのか、と思ったがExpressionStatement
はポインタ型変数ではない。
型アサーションは型名を引数に取ることを思い出すと、構造体のエイリアス型であるExpressionStatement
のポインタ型という意味なのだろうか...? (そうかもしれない)
もしくはこの場合の演算子*
は既存の規則とは分けて考えるべきなのだろうか。
気になる。