- 投稿日:2019-10-26T21:16:56+09:00
tviewで最初の1文字目が表示されない
tviewで最初の1文字目が表示されない
tviewというGo製のOSSがあります。
これはターミナル上でTUIを表現するためのサポートライブラリです。tview のデモの中にtreeviewというものがあり、これを試そうとしたところ1文字目が画像のようにファイル名の1文字目が表示されませんでした。
main.goがain.goとかになっていますね。こうなった場合の解決策が issue にあがっていたので紹介します。
解決策
Treeview's first letter is lostというissueを見つけました。
最初上の画像のような表示がおかしい問題を見た時に何がおかしいのかわからなかったのですが、1文字目が欠けている状態だったみたいですね。issueのコメントを読んでいくとtview text view miss display bug.という別のissueが紹介されています。このissueによればこの問題は
export LC_CTYPE="en_US.UTF-8"をすれば治る。ということです。$ export LC_CTYPE="en_US.UTF-8"これをしたところ私の環境でも正常に表示されていることが確認できました。
LC_CTYPEは システムのLocaleの設定の類でも文字に関する設定のようです。元はja_JP.UTF-8だったのですが、これをen_US.UTF-8に変更することで解決しました。
現在のLocaleについては$ localeコマンドで確認できます。下の出力結果は僕の環境の変更前の結果です。$ locale LANG="ja_JP.UTF-8" LC_COLLATE="ja_JP.UTF-8" LC_CTYPE="ja_JP.UTF-8" LC_MESSAGES="ja_JP.UTF-8" LC_MONETARY="ja_JP.UTF-8" LC_NUMERIC="ja_JP.UTF-8" LC_TIME="ja_JP.UTF-8" LC_ALL=まとめ
今回は特に
tviewで起きた現象についての解決策でしたが、何か文字の表示がおかしいな。と思ったらLocaleの設定等を見直してもいいかもしれませんね。
- 投稿日:2019-10-26T21:10:33+09:00
[Go/gin]macにgoを導入してginを導入してみる
今回、goを使ったapiサーバーを作る機会がありそうなので,それに向けてgoのフレームワークであるginをmacに導入してみたいと思います.goはgooleが開発したコンパイル型言語で安定して使えるらしいです.
環境
- macOS Mojave 10.14.6
GOを導入
$ brew install goGOのバージョン確認
$ go version go version go1.13.3 darwin/amd64go versionでバージョンの確認ができて、正しくインストールされていたら上のように表示されます.
Hello World!の儀式(Go)
新しい言語を扱うときは必ずしないといけない儀式を行います.
任意のディレクトリにhello_world.goを作成して、このように記述しましょう.
goはimportしたものは必ず使わないとコンパイルエラーになるので注意しましょう.hello_world.gopackage main import "fmt" func main() { fmt.Printf("Hello world!!\n") }hello_world.goがあるディレクトリ内で go run hello_world.go とコマンド入力することでコンパイル&実行できます.(コンパイル言語感がなくていいですね笑)
$ go run hello_world.go Hello world!!fmtパッケージはgoで標準入力や標準出力などに必要なものです.
Ginのインストール
Goのwebフレームワークで軽量で有名なものにechoやirisと言ったものがあるらしいですが、今回は名前がかっこいいのでGinにしてみます.
$ go get github.com/gin-gonic/ginこれでインストールされます.
多分HOMEディレクトリgoディレクトリが存在するので、その中にsrc/github.com/gin-gonic があれば正しく入ってます.Hello_World_gin
gin_hello.gopackage main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.String(200, "Hello Gin!!") }) r.Run() }このように gin_hello.go を作成して
$ go run gin_hello.goでサーバーが立ち上がるので,http://localhost:8080/ に移動してみてHello Gin !!と表示されているのを確認しましょう.
- 投稿日:2019-10-26T16:36:56+09:00
xoでできたgoの構造体をxoやsqlに依存しないモデルに書き換えるシェル
概要
業務でWebアプリケーションをGoで書くにあたり、DBのORマッパーにgormを利用しているのですが、gormで各テーブルを扱うにあたりテーブル毎の構造体があると便利です。その構造体を作るにあたり、実際のテーブルからGoの構造体を作成してくれるxo/xoという便利なライブラリがあります。
ただ、xoは便利な反面、作成される構造体に少し調整が必要だったり、使わない関数まで作成してしまいます。
設定等で直せるのかもしれませんが、業務ではシェルを作って一括置換してしまったのでそのシェルを紹介します。なおgormの機能で、逆に構造体からテーブルを作成するというアプローチもできるのですが、構造体にIndexやあたいのデフォルト値等DB固有の値を表現していくのはそこそこ困難なので、テーブルの方を基準として作成するというアプローチを取っています。
環境
DBはMySQL5.7を利用しています。
また、シェルはbash4.0以上でローカルのMac上で実行します。xoで作成される構造体の変更したい部分
xoで作成される構造体を実際に利用するとき変更したい部分は以下になります。
- ファイル名をxxx.xo.goではなくxxx.goにする
- RDBに依存した型を利用したくない
- Mysql.xxx,sql.xxx等
- 将来的にRDB以外を利用することも見据えてDBに依存した型にしたくない
- Nullが入るカラムはポインタ型にして、構造体嬢でもnilとして扱いたい
- gormで扱うときにも問題が発生ない
- 作成された構造体以外使わないので消したい
以下のような生成されたファイルが
// Package model contains the types for schema 'xxx'. package model // Code generated by xo. DO NOT EDIT. import ( "database/sql" "errors" "time" "github.com/go-sql-driver/mysql" ) // User represents a row from 'xxx.users'. type User struct { ID int `json:"id"` // id Nullint sql.NullInt64 `json: "null int"` // null int NullTime mysql.NullTime `json: "null time"` // null time Nullstr sql.Nullstring `json: "null_str"` // str RegisteredAt time.Time `ison: "registered at"` // registered at CreatedAt time.Time `json:"created at"` // created at UpdatedAt time.Time `json: "updated_at"` // updated at // xo fields _exists, _deleted bool } // Exists determines if the User exists in the database. func (u *User) Exists() bool { return u._exists } // Deleted provides information if the user has been deleted from the database. func (u *User) Deleted() bool { return u._deleted } // Insert inserts the user to the database. func (u *User) Insert (db XODB) error { (以下略)以下のようになればOKです。
package model import ( "time" ) // User represents a row from 'xxx.users'. type User struct { ID int `json:"id"` // id Nullint *int `json: "null int"` // null int NullTime *time.Time `json: "null time"` // null time Nullstr *string `json: "null_str"` // str RegisteredAt time.Time `ison: "registered at"` // registered at CreatedAt time.Time `json:"created at"` // created at UpdatedAt time.Time `json: "updated_at"` // updated at }シェル
xoで生成された構造体をdomain/model以下に入れて以下のシェルを実行させます。
#!/usr/local/bin/bash #不要なコメントを削除 grep -l 'type' ./domain/model/*.xo.go | xargs sed -i '' -e 's/\/\/ Package.*$//' grep -l 'type' ./domain/model/*.xo.go | xargs sed -i '' -e 's/\/\/ Code generated by xo.*$//' grep -l 'type' ./domain/model/*.xo.go | xargs sed -i '' -e 's/\/\/ xo fields.*$//' grep -l 'type' ./domain/model/*.xo.go | xargs sed -i '' -e 's/_exists.*$//' grep -l 'type' ./domain/model/*.xo.go | awk '{ sub("m/\/\/ Exists determines.*$",""); print $0; }' #null系カラムの対象 grep -l 'type' ./domain/model/*.xo.go | xargs sed -i '' -e 's/sql\.NullInt64/\*int/g' grep -l 'type' ./domain/model/*.xo.go | xargs sed -i '' -e 's/sql\.NullString/\*string/g' grep -l 'type' ./domain/model/*.xo.go | xargs sed -i '' -e 's/sql\.NullBool/\*bool/g' grep -l 'type' ./domain/model/*.xo.go | xargs sed -i '' -e 's/mysql\.NullTime/\*time.Time/g' grep -l 'type' ./domain/model/*.xo.go | while read file do #構造体以降削除 line=$(grep $file -e "Exists determines" -n | sed -e "s/\(.*\):.*$/\1/g") if [ -n "$line" ]; then cmd="sed -i '' -e '$line,\$d' $file" eval ${cmd} fi #リネーム struct=$(grep $file -e "type \(.*\) struct.*$" | sed -e "s/type \(.*\) struct.*$/\1/g") mv $file ./domain/model/${struct,}.go done # 実際に使用するときには更にgofmtとgoimportをかけてます。 # make fmt # make import最後に
やりたい事はできているのですが力技なので、コードの自動生成をするもっとシンプルなライブラリを作れないかなと思っています。他の良いやり方があればぜひ教えてください。
- 投稿日:2019-10-26T13:41:17+09:00
【備忘録】Go言語のパッケージをMacで使用しようとして躓いた
はじめに
Go言語には便利なパッケージというものが用意されているらしく、使ってみたいと思って使ってみた。今回は、エンジニアであれば、多くの人が利用するgithubのgoパッケージを利用してみた。
しかし、普段PythonやCなど違う言語を使用してきた人間からすると、特にパッケージの利用で躓くと思いました。そこで、githubに公開されている、GithubのGoパッケージgo-githubを例にパッケージの利用について書きたいと思います。環境
macOS Mojave
go 1.13.3作業ディレクトリ
他の言語と異なる点は、作業ディレクトリだと思います。以下のサイトを参考に、ディレクトリを作ってください。GOPATHのsrcに入っていないと上手く動きません。
https://qiita.com/mumoshu/items/0d2f2a13c6e9fc8da2a4パッケージ依存関係管理ツール dep
今回は、パッケージの依存関係ツールを使います。これは、以下のサイトを参考に入れてください。
https://qiita.com/Azizim_A/items/66564b5dc7597717932bgo get -u github.com/golang/dep/cmd/depで入ります。
動かしてみる
今回使うgo-githubのexampleがgithubに転がっているので、それを引用。
以下から手に入る。
https://github.com/google/go-github/tree/master/example/simpleさっき作った作業ディレクトリ直下にmain.goとか適当に作って、さっき手に入れたコードのスクリプトをコピペなりして、main.goへ書く。
その後、作業ディレクトリ(main.goがあるところ)でdep initとすると、main.goを読んで勝手にパッケージを読んでくれる、その後
dep ensureで、パッケージをインストールし
go buildでビルド完成
- 投稿日:2019-10-26T12:57:01+09:00
[GKE] Google Kubernetes Engine + docker compose で開発環境を手に入れる
TL;DR
開発する(frontend)
- frontend ソース更新
- make build
- make start
- ローカルで動作確認
- make deploy でデプロイされる
backend 追加する
- backend フォルダに適当になんか用意
- backend-{deployment,service}.yaml を用意
- Makefile への追記
詳細
公式の guestbook を元に go + redis な開発ができるようにする
https://github.com/kubernetes/examples/tree/master/guestbook-goファイル構成はこんな感じ
frontend/* に対象のサンプルファイルおいた. ├── Makefile ├── README.md ├── data │ └── reis ├── docker-compose.yml ├── frontend │ ├── Dockerfile │ ├── main.go │ └── public │ ├── index.html │ ├── script.js │ └── style.css ├── frontend-deployment.yaml ├── frontend-service.yaml ├── redis-master-deployment.yaml ├── redis-master-service.yaml ├── redis-slave-deployment.yaml └── redis-slave-service.yaml
- 後々 backend/* なり api/* なり ci/* なり拡張しやすい気がする
- pod 定義は公式に習って deployment + service
- go は json で書かれてたけど yaml の方が好きなので yaml で
Makefile から各種更新やらローカル環境ホスティングできるように
update: kubectl apply -f ${FILE} .PHONY: update build: docker build -t gcr.io/${PROJECT_ID}/${FRONTEND} ./frontend # docker build -t gcr.io/${PROJECT_ID}/${BACKEND} ./backend .PHONY: build deploy: gcloud docker -- push gcr.io/${PROJECT_ID}/${FRONTEND} # gcloud docker -- push gcr.io/${PROJECT_ID}/${BACKEND} .PHONY: deploy dev: docker-compose up -d .PHONY: devdocker-compose.yml では redis を master / slave で構成
web は image を立ててポートを調整version: '3' services: web: image: gcr.io/siiid-prd/siiid-frontend:latest ports: - "3341:3000" redis-master: image: redis ports: - "6380:6379" volumes: - "./data/reis:/data" redis-slave: image: redis command: redis-server --slaveof redis-master 6379 ports: - "6381:6379" volumes: - "./data/reis:/data"gke はいいぞ!
- 投稿日:2019-10-26T11:41:34+09:00
Go/echo 入門
はじめに
GoのWebフレームワークecho入門。
公式ドキュメントに書いてあることをほんのちょっとだけ深ぼってみる。Quick Start
なにはともあれ
go getgo get -u github.com/labstack/echo/...結論のコード。
package main import ( "net/http" "github.com/labstack/echo" ) func main() { e := echo.New() e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) e.Logger.Fatal(e.Start(":1323")) }ブラウザから
http://localhost:1323に接続するとHello, World!という文字列が表示されることが確認できます。
echo.New()はこのフレームワークの核となるEchoインスタンスを作成します。
詳細を追うことはしませんが、*http.Serverやnet.Listener等をラップしているようです。
(*Echo) GETはhttp.HandleFuncと同じような役割を担うメソッドです。HTTPメソッドとリクエストを受け取るURLパターン、それに対応するためのハンドラを登録します。
(*Echo) GETで登録されたハンドラはGETメソッドで送信されたリクエストだけを受け取ります。http://localhost:1323に対してPOSTメソッドでリクエストが送信された場合、そのレスポンスはMethod Not Allowedです。
echo.Contextは現在のHTTPリクエスト状況を表しています。http.ResponseWriterや*http.Requestをラップしており、リクエスト内容他、諸々の情報を保持しています。echo.Context represents the context of the current HTTP request. It holds request and response reference, path, path parameters, data, registered handler and APIs to read request and write response.
(*context) Stringはステータスコードとともに文字列をレスポンスに書き込むためのメソッドです。Content-TypeのMINETypeはtext/plain、文字コードはUTF-8が指定されます。
(*Echo) StartはHTTPサーバーを開始するためのメソッドです。指定されたアドレスへのリクエストを待ち受け開始します。実質http.ListenAndServe。Routing
e.POST("/users", saveUser) e.GET("/users/:id", getUser) e.PUT("/users/:id", updateUser) e.DELETE("/users/:id", deleteUser)上の例では無名関数をハンドラとして登録しましたが、
func(echo.Context) errorという型さえ満たせば任意の関数名でハンドラを登録することが出来ます。
ルーティングの機能としては見たままですね。HTTPメソッド、URLパターン、ハンドラを登録します。:idのように書くと、この部分がプレースホルダーになります。Path Parameters
// e.GET("/users/:id", getUser) func getUser(c echo.Context) error { // User ID from path `users/:id` id := c.Param("id") return c.String(http.StatusOK, id) }
(*context) Paramで指定したプレースホルダーから文字列を取り出すことが出来ます。http://localhost:1323/users/JoeにGETメソッドでリクエストを投げると、変数idにJoeという文字列が格納されます。
http://localhost:1323/users/1のようにプレースホルダー部分が数値であったとしても、(*context) Paramは文字列を返すメソッドなので、構造体やDB等と結びつける場合は注意が必要です。Query Parameters
//e.GET("/show", show) func show(c echo.Context) error { // Get team and member from the query string team := c.QueryParam("team") member := c.QueryParam("member") return c.String(http.StatusOK, "team:" + team + ", member:" + member) }
(*context) QueryParamでクエリパラメーターを取得することが出来ます。語ることもないですね...。
http://localhost:1323/show?team=x-men&member=wolverineにGETメソッドでリクエストを投げるとteam:x-men, member:wolverineの文字列が返ってきます。type User struct { Name string `json:"name" xml:"name" form:"name" query:"name"` Email string `json:"email" xml:"email" form:"email" query:"email"` } e.POST("/users", func(c echo.Context) error { u := new(User) if err := c.Bind(u); err != nil { return err } return c.JSON(http.StatusCreated, u) // or // return c.XML(http.StatusCreated, u) })構造体に対して上記のようにタグ付けをすることで、リクエストの
Content-Typeとそのキーに基づいてデータを紐付けるが出来ます。(*context) Bindの使用方法はとても簡単で、タグ付けが行われている構造体のゼロ値のポインタを引数に取るだけです。application/jsonやapplication/x-www-form-urlencodedで該当の値が送られてきた場合、よしなに構造体へのデータ格納を行ってくれます。
(*context) JSONが初めて出てきましたが機能としては(*context) Stringと大差はありません。構造体やマップ等を渡せばjsonへのエンコードを行い、ステータスコードとともにレスポンスへ書き込みます。おわりに
今回はガイドの1ページ目に記載されている本当に基礎的な機能だけを試してみましたが、他にも色々な機能が提供されていて使い勝手が良さそうです。まだまだ使いこなせてないので色々試していきたい。
Echo High performance, extensible, minimalist Go web frameworkとりあえずMiddlewareやValidator機能を試しますかね...
