20191026のGoに関する記事は6件です。

tviewで最初の1文字目が表示されない

tviewで最初の1文字目が表示されない

tviewというGo製のOSSがあります。
これはターミナル上でTUIを表現するためのサポートライブラリです。

tview のデモの中にtreeviewというものがあり、これを試そうとしたところ1文字目が画像のようにファイル名の1文字目が表示されませんでした。 main.goain.go とかになっていますね。

image.png

こうなった場合の解決策が 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の設定等を見直してもいいかもしれませんね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Go/gin]macにgoを導入してginを導入してみる

今回、goを使ったapiサーバーを作る機会がありそうなので,それに向けてgoのフレームワークであるginをmacに導入してみたいと思います.goはgooleが開発したコンパイル型言語で安定して使えるらしいです.

環境

  • macOS Mojave 10.14.6

GOを導入

$ brew install go 

GOのバージョン確認

$ go version
go version go1.13.3 darwin/amd64

go versionでバージョンの確認ができて、正しくインストールされていたら上のように表示されます.

Hello World!の儀式(Go)

新しい言語を扱うときは必ずしないといけない儀式を行います.
任意のディレクトリにhello_world.goを作成して、このように記述しましょう.
goはimportしたものは必ず使わないとコンパイルエラーになるので注意しましょう.

hello_world.go
package 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.go
package 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 !!と表示されているのを確認しましょう.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

xoでできたgoの構造体をxoやsqlに依存しないモデルに書き換えるシェル

概要

業務でWebアプリケーションをGoで書くにあたり、DBのORマッパーにgormを利用しているのですが、gormで各テーブルを扱うにあたりテーブル毎の構造体があると便利です。その構造体を作るにあたり、実際のテーブルからGoの構造体を作成してくれるxo/xoという便利なライブラリがあります。

ただ、xoは便利な反面、作成される構造体に少し調整が必要だったり、使わない関数まで作成してしまいます。
設定等で直せるのかもしれませんが、業務ではシェルを作って一括置換してしまったのでそのシェルを紹介します。

なおgormの機能で、逆に構造体からテーブルを作成するというアプローチもできるのですが、構造体にIndexやあたいのデフォルト値等DB固有の値を表現していくのはそこそこ困難なので、テーブルの方を基準として作成するというアプローチを取っています。

環境

DBはMySQL5.7を利用しています。
また、シェルはbash4.0以上でローカルのMac上で実行します。

(参考)Macのbashを4.x系に変更する

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

最後に

やりたい事はできているのですが力技なので、コードの自動生成をするもっとシンプルなライブラリを作れないかなと思っています。他の良いやり方があればぜひ教えてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】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/66564b5dc7597717932b

go 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

でビルド完成

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[GKE] Google Kubernetes Engine + docker compose で開発環境を手に入れる

TL;DR

開発する(frontend)

  1. frontend ソース更新
  2. make build
  3. make start
  4. ローカルで動作確認
  5. make deploy でデプロイされる

backend 追加する

  1. backend フォルダに適当になんか用意
  2. backend-{deployment,service}.yaml を用意
  3. 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: dev

docker-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 はいいぞ!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go/echo 入門

はじめに

GoのWebフレームワークecho入門。
公式ドキュメントに書いてあることをほんのちょっとだけ深ぼってみる。

Quick Start

なにはともあれgo get

go 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.Servernet.Listener等をラップしているようです。

(*Echo) GEThttp.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.

echo official guide

(*context) Stringはステータスコードとともに文字列をレスポンスに書き込むためのメソッドです。Content-TypeMINETypetext/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メソッドでリクエストを投げると、変数idJoeという文字列が格納されます。
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/jsonapplication/x-www-form-urlencodedで該当の値が送られてきた場合、よしなに構造体へのデータ格納を行ってくれます。

(*context) JSONが初めて出てきましたが機能としては(*context) Stringと大差はありません。構造体やマップ等を渡せばjsonへのエンコードを行い、ステータスコードとともにレスポンスへ書き込みます。

おわりに

今回はガイドの1ページ目に記載されている本当に基礎的な機能だけを試してみましたが、他にも色々な機能が提供されていて使い勝手が良さそうです。まだまだ使いこなせてないので色々試していきたい。
Echo High performance, extensible, minimalist Go web framework

とりあえずMiddlewareやValidator機能を試しますかね...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む