- 投稿日:2020-03-16T22:10:45+09:00
Go の json.Marshal の思いがけない仕様
結論
json.Marshal
は、uint8
型のスライスを base64 文字列にする。
int8
型だったり スライスじゃなくて配列だったりすると配列になるけど。ソースコードと実行結果
golangpackage main import ( "encoding/json" "fmt" ) type S struct { U8Array [3]uint8 U8Slice []uint8 S8Array [3]int8 S8Slice []int8 U8SliceP *[]uint8 PU8Slice []*uint8 IFSlice []interface{} } func main() { a:=uint8(1) b:=uint8(2) c:=uint8(3) o := S{ U8Array: [3]uint8{1, 2, 3}, U8Slice: []uint8{1, 2, 3}, S8Array: [3]int8{1, 2, 3}, S8Slice: []int8{1, 2, 3}, U8SliceP: &[]uint8{1, 2, 3}, PU8Slice: []*uint8{&a, &b, &c}, IFSlice: []interface{}{uint8(1),uint8(2),uint8(3)}, } bytes, _ := json.Marshal(o) fmt.Println(string(bytes)) }実行するとこうなる:
json{ "U8Array":[1,2,3], "U8Slice":"AQID", "S8Array":[1,2,3], "S8Slice":[1,2,3], "U8SliceP":"AQID", "PU8Slice":[1,2,3], "IFSlice":[1,2,3] }"AQID" について
base64 でデコードすると、下記の通り
[1, 2, 3]
になる。rubyrequire "base64" Base64.decode64("AQID").bytes # => [1, 2, 3]まとめ
json.Marshal
でシリアライズすると下表のようになる。
go上の型 json uint8
の配列数値の配列 uint8
のスライスbase64文字列 int8
の配列数値の配列 int8
のスライス数値の配列 uint8
のスライスへのポインタbase64文字列 uint8
へのポインタのスライス数値の配列 interface
のスライスにuint8
だけを詰めたもの数値の配列 思いがけない仕様で大変驚いた。
驚きは2点
uint8
のスライスが base64文字列になる点。uint8
の スライス が base64文字列になるのに、uint8
の 配列 は数値の配列になる点。
- 投稿日:2020-03-16T21:37:19+09:00
パワーワードが作れる?ランダム単語合成アプリを作ってみた
はじめに
クリックするだけ!ランダムに2つの単語を「反応」させて、パワーワードを作りましょう!
モードは
- 形容詞+名詞
- 副詞+動詞
- 名詞+助詞+動詞
の3種類です!
ソースコード
GitHub - Syuparn/LiteralReaction: ランダムに単語を「反応」させて、パワーワードを作ろう
(DockerやVue.jsをはじめて使ったので、汚いところもあると思います…ツッコミ、修正歓迎です)
Webアプリの形で作りましたが、web上では公開していません。
(単語が本当にランダムなので、暴言や不謹慎な表現も生成されるかもしれないからです…)試したい方は上記リンクからダウンロードしてください。docker-composeを使用しているので、
$ docker-compose -f docker-compose.prod.yml up -dでコンテナを起動すれば
localhost:8080
からアクセスできます。webアプリケーションの構成
WebサーバとAPIサーバの2段構成になっています。
WebサーバはUI(html)、APIサーバは単語情報(JSON)をやりとりします。
コンテナもWebサーバとAPIサーバの2つに分けています。
(UIと単語生成を疎結合にして修正しやすくするためです)docker-compose.prod.ymlversion: '3' services: apiserver: build: context: ./apiserver/ dockerfile: Dockerfile ports: - "127.0.0.1:5050:5050" # host:container expose: - "5050" # port for other containers (not host) vue-frontend: build: dockerfile: Dockerfile context: ./vue-frontend ports: - "127.0.0.1:8080:80" # host:containerただし、ユーザーから見ても別々のURLになってしまうと不格好なので、Webサーバの
/api/
ではじまるURLにリクエストされた場合はAPIサーバに転送するようにしています。vue-frontend/nginx_config/default.confserver { listen 80; server_name localhost; server_tokens off; location / { # デフォルトではhtmlを返す root /usr/share/nginx/html; try_files $uri $uri/ @dynamic; } location /api/ { # URLが"/api/"ではじまる場合のみ、APIサーバに転送 # URLのうち"/api/"に続く部分のみを取り出す rewrite /api/(.*) /$1 break; # docker-composeの機能によって、exposeされた他コンテナのポートはアプリケーション名でアクセス可能 proxy_pass http://apiserver:5050; # redirectを無効化(既にrewriteでリダイレクトを設定しているため) proxy_redirect off; proxy_set_header Host $host; } }単語情報GETの流れは以下の通りです。(例は「形容詞+名詞」の場合)
- クライアントは、単語生成(「反応」)ページを開いたときに、形容詞を
localhost:8080/api/rand/adjective
へGETリクエスト- WebサーバがリクエストをAPIサーバ
http://apiserver:5050/rand/adjective
へ転送- APIサーバがリクエストに基づきランダムな形容詞1つをJSON形式で(Webサーバに)返す
- Webサーバはレスポンスをクライアントへ転送
- クライアントに形容詞データのJSONが届く
- 名詞についても同様
単語データ生成
「MeCab IPADIC」を使用しました。
MeCabは、文章を単語(正確には形態素、ことばの最小単位)ごとに分かち書きするソフトウェアです。
そして、MeCabが参照する形態素の辞書の1つがIPADICです。
品詞ごとにcsvファイルで書かれていて、各行に各形態素の表記、読み、品詞、活用などが格納されています。
Adj.csvあらっぽい,19,19,6956,形容詞,自立,*,*,形容詞・アウオ段,基本形,あらっぽい,アラッポイ,アラッポイ あらっぽし,23,23,6956,形容詞,自立,*,*,形容詞・アウオ段,文語基本形,あらっぽい,アラッポシ,アラッポシ あらっぽから,27,27,6956,形容詞,自立,*,*,形容詞・アウオ段,未然ヌ接続,あらっぽい,アラッポカラ,アラッポカラ ...このアプリの単語合成モードは
- 形容詞(終止形)+名詞
- 副詞+動詞(終止形)
- 名詞+(助詞)+動詞(終止形)
の3種類です。
この形式で最大限単語を利用するため、(文法的用語には少し不正確ですが)以下のルールで単語を抽出しました。apiserver/db/format-ipadic-csv.bash# 形容詞として利用 # 形容詞終止形 cat $PATH_FROM/Adj.csv | awk -F"," '$10~/^基本形$/ {print $1}' > $PATH_TO/adj.txt # (「な」を付けると形容詞になる名詞)+「な」 cat $PATH_FROM/Noun.adjv.csv | awk -F"," '{print $1 "な"}' >> $PATH_TO/adj.txt # (「ない」を付けると形容詞になる名詞)+「ない」 cat $PATH_FROM/Noun.nai.csv | awk -F"," '{print $1 "ない"}' >> $PATH_TO/adj.txt # 名詞として利用 # 「ナンセンスさ」を出したいので人名、地名、専門用語は除外し、一般名詞だけ使用 # 名詞 cat $PATH_FROM/Noun.csv | awk -F"," '{print $1}' > $PATH_TO/noun.txt # 「する」を付けると動詞になる名詞 cat $PATH_FROM/Noun.verbal.csv | awk -F"," '{print $1}' >> $PATH_TO/noun.txt # 「な」を付けると形容詞になる名詞 cat $PATH_FROM/Noun.adverbal.csv | awk -F"," '{print $1}' >> $PATH_TO/noun.txt # 「ない」を付けると形容詞になる名詞は単体で使うと不自然なので未使用) # 副詞として使用 # 副詞 cat $PATH_FROM/Adverb.csv | awk -F"," '{print $1}' > $PATH_TO/adverb.txt # 副詞としても使える名詞(「毎日」等) cat $PATH_FROM/Noun.adverbal.csv | awk -F"," '{print $1}' >> $PATH_TO/adverb.txt ## 形容詞連用形(小さい「っ」で終わるものは動詞が後続できないので削除) cat $PATH_FROM/Adj.csv | awk -F"," '$10~/^連用テ接続$/ {print $1}' \ | awk '!/っ$/' >> $PATH_TO/adverb.txt # 動詞として利用 # 動詞 cat $PATH_FROM/Verb.csv | awk -F"," '$10~/^基本形$/ {print $1}' > $PATH_TO/verb.txt # (「する」を付けると動詞になる名詞)+「する」 cat $PATH_FROM/Noun.verbal.csv | awk -F"," '{print $1 "する"}' >> $PATH_TO/verb.txt上記で生成したcsvをSQLiteに流し込んだものを、単語データベースとして使用しました。
apiserver/db/csv2sqlite.bashsqlite3 $SQL_PATH/$SQL_NAME << EOS /* create tables */ .read ./db/init.sql /* let col separator "," */ .separator ',' /* import word data csvs */ .mode csv .import $CSV_PATH/adj.csv adjectives .import $CSV_PATH/adverb.csv adverbs .import $CSV_PATH/noun.csv nouns .import $CSV_PATH/verb.csv verbs EOSapiserver/db/init.sqldrop table if exists adjectives; create table adjectives ( id integer primary key, word text ); drop table if exists adverbs; create table adverbs ( id integer primary key, word text ); drop table if exists nouns; create table nouns ( id integer primary key, word text ); drop table if exists verbs; create table verbs ( id integer primary key, word text );(個人的)面白かった生成結果
意外とフレーズが成立する割合が高く(体感100回に1回くらい?)、他にも以下のような「まともな」フレーズが生成されました。
居場所を失う 酷い蛇行 自我を失う 泥棒を訴える 移り気なロマンチスト やさしいインターフェースはまったところ
docker-compose buildできない
docker-compose
はapt install
でインストールすると古いバージョンが入ってしまいます…そして、古いバージョンのdocker-composeでバージョン3の
docker-compose.yml
を使うとビルドに失敗します。
apt install
ではなくcurl
で最新バージョンをダウンロードしましょうInstall Docker Compose | Docker Documentation
(公式ドキュメントはちゃんと読まねば…反省)
go buildできない
Go1.13からはライブラリ管理の方法が変わったため、最初に
go mod init
をする必要があります。このコマンドを打つと、モジュールの依存関係ファイル
go.mod
やモジュールが本物か確かめるためのチェックサムgo.sum
が生成されます。Go 1.13 に向けて知っておきたい Go Modules とそれを取り巻くエコシステム - blog.syfm
その代わり、
go.mod
のおかげでライブラリを手動でgo get
する必要がなくなりました!
(go build
時に自動でダウンロードされる)コンテナ作成中に生成した実行ファイルが消える
volumes
とCOPY
を混同していました。
Dockerfile
中でCOPY
すると、コンテナのビルド時にホストのディレクトリをコピーします。
docker-compose.yml
中でvolumes
を指定すると、コンテナの起動時にコンテナのディレクトリにホストのディレクトリをマウントします。そのため、ホストのソースを
COPY
してせっかく実行ファイルをビルドしても、同じディレクトリをvolumes
に指定するとホストのディレクトリ(もちろん実行ファイルは無い)に隠されてしまいます…docker-composeのvolumesで指定したホストのディレクトリがマウントされずハマった
conic-gradientが使えない
単語生成画面の集中線はCSSの
conic-gradient
関数を使用する予定でした…が、この関数はFirefoxでは非対応です。
conic-gradient() - CSS: カスケーディングスタイルシート | MDN
そこで、同名のnpmパッケージを使ってjs側で模様を生成しました。
しかし、このパッケージはES5で書かれているのでVue CLI内部で
import
できません。結局、
index.html
で直接グローバルに読み込む- Vue側では
window.ConicGradient
の形で呼び出すnpm run build
ではビルドされないので、別途モジュールをstaticにコピーするという手順を取りました。
vue-frontend/index.html<!DOCTYPE html> <html> ... <body> <div id="app"></div> <!-- built files will be auto injected --> <!-- ここでモジュールを読み込む(prefixfreeはconic-gradientの依存モジュール) --> <script src="./static/prefixfree.min.js"></script> <script src="./static/conic-gradient.js"></script> </body> </html>vue-frontend/src/components/bangBackground.vue<script> export default { name: 'bangBackground', data: function () { return { bangSVG: new window.ConicGradient({ repeating: true, stops: `#ffffff 0, #ffffff 2.0%, #AAAAAA 2.125%, #AAAAAA 2.375%, #ffffff 2.5%` }) } } } </script>vue-frontend/DockerfileFROM node:lts-alpine as builder RUN mkdir -p /app WORKDIR /app RUN npm install -g http-server COPY ./package*.json ./ RUN npm install \ # conic-gradientモジュールをstaticにコピー && mkdir -p static/ \ && cp node_modules/conic-gradient/conic-gradient.js static/ \ && cp node_modules/prefixfree/prefixfree.min.js static/ COPY . . RUN npm run build FROM nginx:stable-alpine as product COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx_config/default.conf /etc/nginx/conf.d/default.conf CMD ["nginx", "-g", "daemon off;"]参考文献
Go言語
APIサーバ
golangでREST APIをやってみた① - QiitaSQLiteとの接続
golangでSQLite3を使ってデータベースを操作する方法まとめ | Black Everyday CompanyMeCab
IPADICダウンロード方法
Alpine LinuxでMeCab with NEologd - QiitaVue.js
SPA(シングルページアプリケーション)のつくり方
【Vue.js】爆速でSPAを作る - Qiitaaxiosを使ったJSONのGET/POST
Vue-CLIのプロジェクトでaxiosを使ってAPIや外部リソースからのデータを取得する | 大阪市天王寺区SOHOホームページ制作 | デザインサプライ-DesignSupply.-コンポーネント内部に要素を入れる(公式)
スロット — Vue.jsnginx
URLの一部分だけ取り出してポートフォワーディング(
/api/hoge -> apiserver:5050/hoge
)
Nginx reverse proxy + URL rewrite - Server FaultDocker,docker-compose
docker-compose基本操作
docker-compose コマンドまとめ - Qiita複数サーバ(複数コンテナ)を協調させる方法
マイクロサービスほどじゃないけどウェブサービスを分割開発したい人向けDocker設定を集めるスレ - Qiitavue-cliビルド方法(公式)
Vue.js アプリケーションを Docker 化する — Vue.jsGolangマルチステージビルド方法
(マルチステージビルドのメリット)
Dockerのマルチステージビルドを使う - Qiita
(Go1.7の記事ですが、Go1.13以上の場合Dockerfileでgo get
をする必要はありません)(go-sqlite3ライブラリを使う場合)
go-sqlite3 が入った状態での Docker のマルチステージビルドを行う - Pistatium note
- 投稿日:2020-03-16T20:00:21+09:00
Docker + VSCodeでGoの環境構築(ホットリロード対応)
はじめに
個人開発で最近Go言語を使う機会が増えてきました。
Go言語で開発するにあたり私が感じたことは、
- Dockerで動かしたい
- ホットリロード機能が欲しい
- 開発用・本番用でコンテナを分けたい
- 開発時は、VSCode上でコンテナ内とリモート接続して作業したい
色々な方々のDocker環境を参考にDockerでGo環境を作りました。
よろしければ参考にしてください。ホスト環境
- Docker: ver.19.03.8
- docker-compose: ver.1.25.4
- VSCode: ver.1.43.0
ホットリロード
Go言語でホットリロードをする際、調べていたら、
- fresh
- realize
があるのを知りました。
しかし、筆者はrealize の方はエラー(error: returned a non-zero code: 1
)が発生してgo get
ができませんでした。一度コンテナを立ち上げて、コンテナ内で
go get
したらうまく行くのですが解決出来ませんでした...ですので今回はfresh を使用します。
手順
- Dockerfile(開発用&本番用) + docker-compose.yml の作成
- Remote Containers の設定
- ホットリロードの設定
- 開発環境の起動
- 開発環境の動作確認
- 本番環境の起動
開発環境
はじめに作業ディレクトリを作ります。
/# 作業ディレクトリ作成 $ mkdir go-work # 作業ディレクトリへ移動 $ cd go-work1. Dockerfile(開発用&本番用) + docker-compose.yml の作成
続いてDockerfile とdocker-compose.yml を書いていきます。
下記コマンドからファイルを作成してください。/go-work$ touch docker-compose.yml $ mkdir -p docker/golang $ touch docker/golang/Dockerfile.dev $ touch docker/golang/Dockerfile.dep※ Dockerfileの言語サポート
Dockerfile.dev(.dep) はVSCodeの標準では言語サポートされていないので設定を追加します。
設定されている方は次へ飛ばしてください。
- [Code] -> [基本設定] -> [設定]
Files: Associations
で検索settings.json
で編集settings.json{ "files.associations": { "Dockerfile.dev": "dockerfile", "Dockerfile.dep": "dockerfile" } }Docker + docker-composeの設定
- 開発用Dockerfile
/go-work/docker/golang/Dockerfile.devFROM golang:1.14.0-alpine3.11 SHELL ["/bin/ash", "-c"] WORKDIR /go/src/app COPY ./app ./ EXPOSE 8080 ENV GO111MODULE=on RUN apk add --no-cache alpine-sdk # Golang ホットリロード(freshのインストール) RUN go get github.com/pilu/fresh # Golang 環境構築(任意) RUN go get github.com/go-delve/delve/cmd/dlv \ github.com/rogpeppe/godef \ golang.org/x/tools/cmd/goimports \ golang.org/x/tools/cmd/gorename \ sourcegraph.com/sqs/goreturns \ github.com/ramya-rao-a/go-outline \ golang.org/x/tools/gopls@latest
- 本番用Dockerfile
/go-work/docker/golang/Dockerfile.depFROM golang:1.14.0-alpine3.11 as builder WORKDIR /go/src/app COPY ./app ./ ENV GO111MODULE=on RUN go mod download RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/app FROM alpine:latest RUN apk --no-cache add ca-certificates COPY --from=builder /go/bin/app /go/bin/app ENTRYPOINT [ "/go/bin/app" ] EXPOSE 8080
- docker-compose
/go-work/docker-compose.ymlversion: '3.7' services: app: container_name: go-app build: context: . # Remote Containerが立ち上がるまで待機 dockerfile: docker/golang/Dockerfile.dev ports: - 8080:8080 volumes: - ./app:/go/src/app:cached command: /bin/ash -c "while sleep 1000; do :; done"2. Remote Containers の設定
インストール
インストールしていない方は、 Remote-Containers からインストールしてください。
準備
まずはじめにVSCode の左下のアイコンをクリックしてください。
すると、コマンドパレッドが開きますので、
Remote-Containers: Add Development Container Configuration Files...
と検索して選択してください。選択したら、
From 'docker-compose.yml'
を選択してください。選択後
.devcontainer
ディレクトリが作成され、設定ファイルが格納されています。設定
.devcontainer ├── devcontainer.json └── docker-compose.ymlまずはじめに、docker-compose.yml はもう作成してあるので、
.devcontainer 内のdocker-compose.yml は削除してください。下記のコマンドを実行してファイルを削除してください。
/go-work$ rm .devcontainer/docker-compose.yml次にdevcontainer.json を編集します。
/go-work/.devcontainer/devcontainer.json{ "name": "go", "dockerComposeFile": [ "../docker-compose.yml" ], "service": "app", "workspaceFolder": "/go/src/app", "settings": { "terminal.integrated.shell.linux": "/bin/ash", "go.gopath": "/go" }, "extensions": [ "ms-vscode.go", ], "shutdownAction": "stopCompose" }3. ホットリロードの設定
準備
まずはGoアプリを開発する作業ディレクトリを作成します。
その後ホットリロードを行うためのfresh の設定ファイルを作成します。ちなみに設定ファイルを作成しなくても大丈夫ですのでこちらは任意で!
下記のコマンドを実行してください。
/go-work$ mkdir app $ cd app # /go-work/app $ touch .fresh.conf設定
/go-work/app/.fresh.confroot: . tmp_path: ./tmp build_name: runner-build build_log: runner-build-errors.log valid_ext: .go, .tpl, .tmpl, .html no_rebuild_ext: .tpl, .tmpl, .html ignored: assets, tmp build_delay: 600 colors: 1 log_color_main: cyan log_color_build: yellow log_color_runner: green log_color_watcher: magenta log_color_app:上記の設定はテンプレートから持ってきています。
細かい設定は任意で!4. 開発環境の起動
さて、いよいよコンテナを起動します。
VSCode から左下のアイコンをクリックしてください。すると、コマンドパレッドが開きますので、
Remote-Containers: Reopen in Container
を検索して選択してください。すると、画面が切り替わります。
この時点でDockerイメージが作成され、コンテナが自動で起動します。
起動するまでしばらく時間がかかりますので待機...しばらくするとコンテナとリモートで繋がり作業ディレクトリに移ります。
VSCode の左下のアイコンも変わっているはずです。
VSCode 上でターミナルを開くと、コンテナ内のターミナルに切り替わっています。コンテナ内/go/src/app #
5. 開発環境の動作確認
ここからはコンテナ内で作業していきます。
ホットリロードの動作を確認するためにGo のプログラムを開発していきます。main.go の作成
開発するためのファイルを作成します。
/go/src/apptouch main.go go mod init main
つづいてmain.go に簡単なWebサーバを作成します。
/go/src/app/main.go// ...省略 func init() { http.HandleFunc("/", index) } func index(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello World") } func main() { fmt.Print("Server Start") if err := http.ListenAndServe(":8080", nil); err != nil { panic(err) } }実行
main.go を実行するのですが今回はfresh を使って実行します。
VSCode 内のコンテナで実行しても良いのですが、こちらは開発時で利用したいので今回は別ターミナルで実行します。別ターミナルでコンテナ内に入り、ホットリロードを起動させます。
別ターミナル$ docker exec -it go-app /bin/ash # コンテナ内: /go/src/app fresh -c .fresh.confmain.go が実行されますので確認作業に移ります。
確認
確認は、ブラウザ または、ターミナル で確認します。
- ブラウザの場合は、http://localhost:8080 にアクセス
- ターミナルの場合は、
curl http://localhost:8080
を実行
Hello World
が返ってくれば起動しています。変更
つづいてホットリロードが正常に起動しているかを確認したいので、
fresh を起動したままVSCode に戻ってください。VSCode上でmain.go を変更し、保存してください。
/go/src/app/main.gofunc index(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, "Hello World") + fmt.Fprint(w, "Hello Golang") }保存したら再度、確認作業を行ってください。
Hello Golang
が返ってくればホットリロードは正常に起動しています。6. 本番環境の起動
本番環境はDockerをビルドして実行すれば良いだけです。
イメージのビルド
/go-work$ docker build -t go-app -f ./docker/golang/Dockerfile.dep .コンテナの実行
/go-work$ docker run --name go-app go-app -p 8080:8080 -d . /bin/ash
- 投稿日:2020-03-16T19:49:35+09:00
【Go】MySQLのTimestamp型カラム(日本時間が入ってる)をtime.TimeにするとUTCになってしまうのを対応する
こんにちは
MySQLのTimestamp型カラムに
2020-03-12 15:00:00
(日本のおやつの時間!)が入っていて、それをGo言語のコードでtime.Time
型として取ってくると"2020-03-12 15:00:00 +0000 UTC"
になってしまう。あるとおもいます。
それを期待通りの値に直す小ネタです。
例.go… tempTime := dbResults[0].CreatedAt location := time.FixedZone("JST", 9*60*60) tempTime = tempTime.In(location).Add(-9*time.Hour) log.Print(tempTime)
- 強引に
JST
に変換します。- そのとき勝手に9時間足されてしまうので、自前で引き直します。
↓
2020/03/16 19:44:57 2020-03-12 15:00:00 +0900 JSTやったぜ。
- 投稿日:2020-03-16T17:04:29+09:00
Go言語でSliceに要素が存在するか調べるには、要素個数を見るだけで良い ( #go : Error: slice index out of range <nil> )
if len(mySlice) > 3 { // ... }How to check if a slice has a given index in Go? - Stack Overflow
Original by Github issue
- 投稿日:2020-03-16T16:31:31+09:00
Go言語でSliceを要素に持つStructを定義する例 ( #Go )
- 投稿日:2020-03-16T16:15:56+09:00
Go 言語で declared but not used のエラーを回避したい。未使用変数だが名前はつけておきたい時は? ( #go )
- 投稿日:2020-03-16T15:18:58+09:00
Go言語のdelve でブレークポイントを設定してデバッグ実行する ( #go / dlv cli / dlv debug / set break point and debug with single go lang file )
ファイル
こんなファイルがある場合
package main import ( "fmt" ) func main() { fmt.Println("A") fmt.Println("B") fmt.Println("C") fmt.Println("D") fmt.Println("E") foo() } func foo() { value := "FOO" fmt.Println(value) }delveの実行
go run みたいにファイル名を指定して実行する
dlv debug go/dlv/main.goブレークポイントの指定
ファイル名+行数指定で3つのブレークポイントを指定してみる
例:
break main.go:9
(dlv) break main.go:8 Breakpoint 1 set at 0x10c23af for main.main() ./go/dlv/main.go:8 (dlv) break main.go:10 Breakpoint 2 set at 0x10c2485 for main.main() ./go/dlv/main.go:10実行
continue すると、ひとつずつ進める!
ブレークポイントの残りがなくなり、スクリプトが処理を完了すると、終了する。(dlv) continue > main.main() ./go/dlv/main.go:8 (hits goroutine(1):1 total:1) (PC: 0x10c23af) 3: import ( 4: "fmt" 5: ) 6: 7: func main() { => 8: fmt.Println("A") 9: fmt.Println("B") 10: fmt.Println("C") 11: fmt.Println("D") 12: fmt.Println("E") 13: (dlv) continue A B > main.main() ./go/dlv/main.go:10 (hits goroutine(1):1 total:1) (PC: 0x10c2485) 5: ) 6: 7: func main() { 8: fmt.Println("A") 9: fmt.Println("B") => 10: fmt.Println("C") 11: fmt.Println("D") 12: fmt.Println("E") 13: 14: foo() 15: } (dlv) continue C D E FOO Process 54908 has exited with status 0再実行
restart で最初から実行できる!
(dlv) restart Process restarted with PID 54942ブレークポイントをすべてクリアする
(dlv) clearall Breakpoint 1 cleared at 0x10c23af for main.main() ./go/dlv/main.go:8 Breakpoint 2 cleared at 0x10c2485 for main.main() ./go/dlv/main.go:10function + 行数指定でブレークポイントを設定する
(dlv) break foo:2 Breakpoint 3 set at 0x10c2616 for main.foo() ./go/dlv/main.go:19(dlv) continue A B C D E > main.foo() ./go/dlv/main.go:19 (hits goroutine(1):1 total:1) (PC: 0x10c2616) 14: foo() 15: } 16: 17: func foo() { 18: value := "FOO" => 19: fmt.Println(value) 20: }変数の中身を表示してみる
Go言語の文法が使えるわけじゃなくて CLI のコマンドで表示させるみたいだ(dlv) print value "FOO"ブレークポイントの指定方法
いくつかあるみたいだ。
delve/locspec.md at master · go-delve/delve
(ここに書いたやり方だと、なぜかcurrent file を認識してくれていないが、まだ使い始めたばかりなのでよく分かっていない)
参考
Original by Github issue
- 投稿日:2020-03-16T14:04:48+09:00
フロント・バックエンドサービスをコンテナ化してもGitコミット時にLefthookでテストやLint実行
TL;DR
- フロント・バックエンドサービスをそれぞれコンテナ化、docker-composeで全てのコンテナを管理する
- monorepoで管理した際に1リポジトリとなるので気軽にGit Hookの処理ができない
- Lefthookを導入してpre-commit時にすべてのコンテナに対してLintツールを動作させるようにした
サンプルコード
https://github.com/MegaBlackLabel/lefthook-docker-node-go-dev-sample
1リポジトリで開発環境を管理したい
渋川さんの記事
マイクロサービスほどじゃないけどウェブサービスを分割開発したい人向けDocker設定を集めるスレ
https://qiita.com/shibukawa/items/fd49f98736045789ffc3を読んでフロントエンドとAPIがごっちゃになっている開発環境ヨクナイ!ってことでサービス単位でコンテナ化してvs codeのリモートコンテナ機能を使って開発環境を再構築をしていたらGitとGItHooksの扱いで躓く。
Git Hooksの扱い
1リポジトリでサービスをコンテナ化してmonorepoを構築した際に.gitはルートディレクトリのみに存在する。
何が起こるかというと、フロントエンド開発時に「Husky+lint-stagedでコミット前にeslintやprettierを実行してコミット前にソースをチェックする」ができなくなります。これは治安が悪くなるってことで調査を進めた結果、試してみたのがLefthookです。Lefthookを使ってみる
Lefthookとは?ということで公式の説明を引用
The fastest polyglot Git hooks manager out there
Fast and powerful Git hooks manager for Node.js, Ruby or any other type of projects.
- Fast. It is written in Go. Can run commands in parallel.
- Powerful. With a few lines in the config you can check only the changed files on pre-push hook.
- Simple. It is single dependency-free binary which can work in any environment.
- GO製。コマンドを並列実行できる
- かんたんな設定ファイルでpre-pushのhookが使えるようになる
- (GOによる)シングルバイナリなので、どのOSでも実行可能
今回は以下のことを実装しました。
- 起動時にdocker-composeでインスタンス起動
- 起動したインスタンスに対してコマンドを実施
- 更新対象のファイルの拡張子をgrepして対象の拡張子がstageにあるときのみ実行する
※、余談ですが日本語での紹介記事は2つのみ。1つは公式の翻訳ともう一つはRubyでのHusky置き換え記事です。
Lefthook: 多機能GItフックマネージャ
https://techracho.bpsinc.jp/hachi8833/2019_10_16/79052Git HooksマネージャーのLefthookを試してHusky(+lint-staged)と比較した結果、乗りかえました
https://blog.solunita.net/posts/change-lefthook-instead-of-lintstaged-with-husky/Lefthookインストール
Lefthookインストールですが、公式サイトのInstallationか、リリースページから直接ダウンロードします。自分はWindows環境なのでプロジェクト内にlefthook.exeをそのまま置いて使っています。
インストール後に対象のリポジトリで以下のコマンドを実行lefthook install #Windowsで直下に置いている場合は lefthook.exe installインストールが完了するとリポジトリのルートに「lefthook.yml」が作成されますので、こちらに設定を書きます。
Lefthook設定ファイル解説
lefthook.yml# EXAMPLE USAGE # Refer for explanation to following link: # https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md # # pre-push: # commands: # packages-audit: # tags: frontend security # run: yarn audit # gems-audit: # tags: backend security # run: bundle audit # # pre-commit: # parallel: true # commands: # eslint: # glob: "*.{js,ts}" # run: yarn eslint {staged_files} # rubocop: # tags: backend style # glob: "*.rb" # exclude: "application.rb|routes.rb" # run: bundle exec rubocop --force-exclusion {all_files} # govet: # tags: backend style # files: git ls-files -m # glob: "*.go" # run: go vet {files} # scripts: # "hello.js": # runner: node # "any.go": # runner: go run pre-commit: piped: true commands: 1_docker-compose: root: . run: docker-compose up -d 2_eslint: root: "containers/frontend/" glob: "*.{js,jsx,ts}" run: docker exec -it frontend-container yarn eslint-check 3_frontend-test: root: "containers/frontend/" glob: "*.{js,jsx,ts}" run: docker exec -it frontend-container yarn test 4_api-test: root: "containers/api/" run: docker exec -it api-container go testサンプルとインデントの数が違うのはご愛嬌。今回使っている処理は以下の通り。
- pre-commit: コミット実施前に実行してほしい処理
- piped:commandsを名前順に実施する。そのため頭文字に数字をつける
- commands:実行するコマンド
- root:どの階層でコマンドを実施するかを記載
- glob:stagedのファイルのうち、コマンド実行対象とするファイルを選別する
- run:実行するコマンド
コマンドの内容ですが以下の処理を実施しています。
- docker-composeでコンテナを起動
- docker execを使用してフロントエンドコンテナでeslint+Prettierを実施
- docker execを使用してフロントエンドコンテナでテストを実施
- docker execを使用してAPIコンテナでテストを実施
フォルダ・ファイル構成
以下の想定でファイル構成を行っています。
- フロントエンドとAPIサーバをそれぞれコンテナ化して管理
- docker-composeでコンテナを一元管理
- フロントエンドではeslint+Prettierでのソース整形とテストを実施
- APIサーバではテストを実施
- それぞれ正常に実行時のみコミットを実施するファイル構成lefthook-docker-node-go-dev-sample │ .gitignore │ docker-compose.yml │ lefthook.exe │ lefthook.yml │ LICENSE │ README.md │ └─containers ├─api │ docker-entrypoint.sh │ Dockerfile │ go.mod │ go.sum │ main.go │ main_test.go │ └─frontend │ .eslintrc.json │ docker-entrypoint.sh │ Dockerfile │ package.json │ README.md │ yarn.lock │ ├─node_modules ├─public │ favicon.ico │ index.html │ logo192.png │ logo512.png │ manifest.json │ robots.txt │ └─src App.css App.js App.test.js index.css index.js logo.svg serviceWorker.js setupTests.jsLefthookでpre-commit時にコンテナに対してコマンド実行
pre-commitをrunしてみる。
実行結果.\lefthook.exe run pre-commit RUNNING HOOKS GROUP: pre-commit EXECUTE > 1_docker-compose api-container is up-to-date frontend-container is up-to-date EXECUTE > 2_eslint yarn run v1.22.4 $ eslint --print-config .eslintrc.json | eslint-config-prettier-check No rules that are unnecessary or conflict with Prettier were found. Done in 0.69s. EXECUTE > 3_frontend-test yarn run v1.22.4 $ CI=true react-scripts test PASS src/App.test.js ✓ renders learn react link (39ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 2.081s Ran all test suites. Done in 3.23s. EXECUTE > 4_api-test [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET /ping --> github.com/MegaBlackLabel/lefthook-docker-node-go-dev-sample.setupRouter.func1 (3 handlers) [GIN] 2020/03/15 - 03:43:28 | 200 | 44.742µs | | GET /ping PASS ok github.com/MegaBlackLabel/lefthook-docker-node-go-dev-sample 0.014s SUMMARY: (done in 7.78 seconds) ✔️ 1_docker-compose ✔️ 2_eslint ✔️ 3_frontend-test ✔️ 4_api-testコマンドが順番に実行されそれぞれの実行結果が表示。最後にサマリーとしてOK・NGが出力されます。
まとめ
- サービス単位でコンテナ化して開発するのは便利だね。でもGit Hooksの処理ができない
- Lefthook使えばできるよ。シングルバイナリだから導入もかんたんだよ
- コンテナ化してもGit Hooksが使えるので複数の開発者がいても治安が維持できそう
以上
- 投稿日:2020-03-16T11:57:41+09:00
Dockerコンテナ上で動くGinサーバーにアクセスできないエラーの解決法
概要
GolangのWebフレームワークであるGinを使ったAPIサーバーを、Dockerコンテナ上にデプロイして動かそうとしたところ、APIにアクセスできずかなり長い時間悩まされました。
結果的には、Ginサーバーのコードの書き方の問題だったことが分かったのですが、解決方法を念のためここにメモしておきます。
ちなみに、このエラーはWindows10及びAWS上のUbuntuサーバー(t2.small)で起こりました(尤も、実行環境はこのエラーの発生にあまり関係ないようでしたが)。
状況
Ginを用いたAPIサーバーを立てようとしていました。まだ環境構築の段階なので、コードは以下のようなモックのものになっています。
package main import ( "log" "os" "github.com/gin-gonic/gin" ) func main() { logConfig() r := gin.Default() r.GET("/accounting-api", func(c *gin.Context) { log.Println("GET") c.JSON(200, gin.H{ "state": "success", }) }) r.DELETE("/accounting-api", func(c *gin.Context) { log.Println("DELETE") c.JSON(200, gin.H{ "state": "success", }) }) log.Println("Start Server") r.Run() } func logConfig() { logFile, _ := os.OpenFile("log/log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) log.SetOutput(logFile) log.SetFlags(log.LstdFlags | log.Lmicroseconds | log.Lshortfile) log.SetPrefix("[LOG] ") }単純に、
GET
かDELETE
メソッドで特定のパスへのリクエストが来たら、{"state": "success"}
というjsonを返すだけのサーバーです。そして、このサーバーを動かすためのDockerfileが以下です。
FROM golang:alpine RUN apk update && apk add --no-cache git RUN go get -u github.com/gin-gonic/gin && mkdir /usr/src && mkdir /usr/src/api COPY ./api /usr/src/api WORKDIR /usr/src/api CMD ["go","run","main.go"]ホスト上の
api
というディレクトリには上記のGoファイル等があるため、それをコンテナ上にコピーして、サーバーを立ち上げます。このDockerfileをapi
という名前でビルドして、それを以下のコマンドで立ち上げました。docker run -p 8083:8080 api
ホスト上のポート
8083
をコンテナ上のポート8080
にマッピングしています。上記のコマンドを実行すると、以下のような出力がなされ、Ginサーバーが立ち上がっていることが確認できます。[GIN-debug] GET /accounting-api --> main.main.func1 (3 handlers) [GIN-debug] DELETE /accounting-api --> main.main.func4 (3 handlers) [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default [GIN-debug] Listening and serving HTTP on localhost:8080ポートを指定しなかったので、デフォルトでポート
8080
で動いています。このように一見ちゃんと動いているようにも見えますが、ホスト側でhttp:localhost:8083
にcurlでアクセスしてみても、以下のようにエラーが出てしまいました。curl: (52) Empty reply from serverエラー解消のため試みたこと
1. コンテナの中に入って、APIにアクセスできるか確認
まず最初に、コンテナ上で本当にGoのプログラムがちゃんと動いているのかを確認します。そのために立ち上げたdockerコンテナの中に入ってみます。
# apiサーバーの動くコンテナの中に入る docker exec -it api /bin/ashこのコンテナのベースとなっているAlpineには
/bin/bash
がなかったので、/bin/ash
を使います。そして以下のコマンドを打って、ちゃんとプログラムが動いているのか確かめます。# そもそもcurlが入っていないので、インストールする apk add --no-cache curl # 念のためプロキシを無効にして、curlでapiサーバーにアクセスする curl -x "" http://localhost:8080/accounting/apicurlの実行結果がこちら。
{"state": "success"}ちゃんと結果が取れています。なので、コンテナ上ではちゃんとGinサーバーのプログラムが動いているようです。
2. DockerfileでポートをEXPOSE
コンテナ上ではプログラムは動いているようなので、次はポートマッピングの部分がうまくいっていないのではないかと疑ってみます。調べてみるとDockerfileには
EXPOSE
というコマンドが書けるようなので、追加してみます。EXPOSE 8080これをやっても、解決しませんでした。
そもそも公式ドキュメントによると、
EXPOSE
コマンドは実際は何の働きもせず、特定のポートを開放する旨を開発者に知らせるための、ドキュメントのような役割しかもっていないようです。なので、EXPOSE
コマンドをつけただけで問題が解決するはずがありませんでした。3. Windowsのファイアウォールの設定の確認
開発は基本的にWindows上のDockerで行っていたため、Windowsのファイアウォールの設定を見直してみましたが、これも意味がありませんでした。
そもそも、このDockerfileをUbuntu上でビルドして立ち上げてみても、同様にAPIサーバーにアクセスできなかったため、最初からなんとなくWindowsのファイアウォールのせいではないことが分かっていましたが。
4. (これで解決)Ginサーバー側でポートの指定
Goのプログラムの中で、GInサーバーを立ち上げる部分でポートを指定するようにしたところ、上手くアクセスできるようになりました。具体的には、以下の部分です。
r := gin.Default() r.Run(":8080")何も指定しなくてもデフォルトで
8080
で立ち上がるため気にしていなかったのですが、しっかりと指定しないとどうやらダメなようです。なぜポートはデフォルトではダメで、明示的に記さなければならないかは、よくわかりません。分かったら追記します。
- 投稿日:2020-03-16T11:47:51+09:00
Go 言語でトランプのStructを作って、カードを強い順に並び替える ( #go lang sort card Struct )
package main import ( "fmt" "sort" ) type Card struct { Strong int Name string } func main() { cards := []Card{ Card{2, "2"}, Card{3, "3"}, Card{4, "4"}, Card{5, "5"}, Card{6, "6"}, Card{7, "7"}, Card{8, "8"}, Card{9, "9"}, Card{10, "10"}, Card{11, "J"}, Card{12, "Q"}, Card{13, "K"}, Card{14, "A"}, } sort.SliceStable(cards, func(i, j int) bool { return cards[i].Strong > cards[j].Strong }) fmt.Println(cards) } // [{14 A} {13 K} {12 Q} {11 J} {10 10} {9 9} {8 8} {7 7} {6 6} {5 5} {4 4} {3 3} {2 2}]Original by Github issue
- 投稿日:2020-03-16T10:29:42+09:00
#Go 言語でシャッフルした52枚のトランプを1枚ずつ配るサンプル ( Go lang deal shuffle playing cards example )
package main import ( "fmt" "math/rand" "time" ) func main() { cards := generateCards() shuffleCards(cards) yourCards := []string{} for _, card := range cards { yourCards = append(yourCards, card) fmt.Println("Your cards are ...") fmt.Println(yourCards) } } func generateCards() []string { cardSeeds := []string{"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"} cards := []string{} for i := 0; i < 4; i++ { cards = append(cards, cardSeeds...) } return cards } func shuffleCards(cards []string) { rand.Seed(time.Now().UnixNano()) rand.Shuffle(len(cards), func(i, j int) { cards[i], cards[j] = cards[j], cards[i] }) }result example
Your cards are ... [5] Your cards are ... [5 3] Your cards are ... [5 3 8] Your cards are ... [5 3 8 J] Your cards are ... [5 3 8 J Q] Your cards are ... [5 3 8 J Q 9] Your cards are ... [5 3 8 J Q 9 9] Your cards are ... [5 3 8 J Q 9 9 6] Your cards are ... [5 3 8 J Q 9 9 6 5] Your cards are ... [5 3 8 J Q 9 9 6 5 4] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6 10] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6 10 J] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6 10 J 6] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6 10 J 6 K] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6 10 J 6 K 8] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6 10 J 6 K 8 2] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6 10 J 6 K 8 2 Q] Your cards are ... [5 3 8 J Q 9 9 6 5 4 4 8 3 7 Q A 4 9 9 7 2 A A 10 5 6 K 2 A Q 4 J 3 5 10 2 8 7 J 10 7 K K 6 10 J 6 K 8 2 Q 3]Original by Github issue
- 投稿日:2020-03-16T10:15:45+09:00
Go 言語で "declared but not used" のエラーを無効にするには 未使用変数をアンダースコアだけにすれば良いじゃない ( #go lang ignore ))
- 投稿日:2020-03-16T10:15:44+09:00
Go 言語で無限ループしながら コンソールのユーザー入力を待ち受ける ( #Go wait user input in console with infinite loop )
package main import ( "bufio" "fmt" "os" ) func main() { for { fmt.Println("Enter some words!") input := bufio.NewScanner(os.Stdin) input.Scan() fmt.Println("input is " + input.Text()) } } // Enter some words! // A // input is A // Enter some words! // B // input is B // Enter some words! // C // input is C // Enter some words!Original by Github issue
- 投稿日:2020-03-16T10:13:38+09:00
go.modってどこにおくのがいいのだろう
go.modってどこにおくのがいいのか
先日一からgoでサービスを作っていたのですが、go.modの置き場で迷ってしまい少し時間を無駄遣いした時に、意外と日本語の情報がなかったので残そうと思います。
結論:ルートディレクトリに置く
結論からいうと、プロジェクトのルートディレクトリにおくのが正解でした。
go modはもともと業務でも使っていたのである程度知識はあると思っていたのですが、いざ自分で初めから作るとなるとどこでgo mod init
するんだ?ってなってしまいました。勉強不足です。色々とググってみたのですが、あまりgo.modをおく場所に関する情報はなくて、自分で色々いじってみてなんとか解決しました。
解決した後に1次ソースには当たっていなかったので(まず当たれという話ですが、、)1次ソースをみてみるとそう行った記述がありました。A module is a collection of Go packages stored in a file tree with a go.mod file at its root.ざっくり訳すと
モジュールとは、ルートディレクトリにgo.modがあるファイルツリーに保存されたGoのパッケージのコレクションであるという感じでしょうか。つまり、ルートディレクトリにgo.modを置くのが正解。ということでした。
自分でいじってみて、もうちょっと突っ込んでみる
初め、自分は1次ソースに当たらずに自分で調べたりいじったりしたので、自分なりの理解も得られました。
そもそも、
go.mod
がルートディレクトリにないとどうなるのか例えば、こんな構成のコードがあったとします。
Service | ー handler ー model ー daoこういう構成であれば、handlerからdaoをimportする時、こう書きたいですよね
import "Service/dao"ですが、例えば、go.modをhandler,model,daoの各ディレクトリ内に置いてしまうと、コンパイルした時にhandler,model,daoが別の外部パッケージとして見なされてしまい、「gorootかgopath内にそんなパッケージないよ」といった旨のエラーが吐かれます。
(私はこれが、gorootやgopahtにないというエラー内容から、go.modの配置の問題であることに気づくのが遅れました。)
また、エディタ上でもgo.modを参照して依存を解決している場合、赤くなると思います。
これを、Service配下にgo.modを一つ置くようにすることでhandler,model,daoが同一パッケージないのものとみなされ、上のimport文で解決できるようになります。
反対に、自分で作ったパッケージをサブパッケージみたいに外部パッケージとして読み込みたい場合は、上の例であれば、handler,models,dao内にgo.modをおいて、giuhubとかに上げて、import "github.com/user_name/repository_name/..."と書いてあげれば、良さそうなこともわかりました。
最後に
go.modは自分が同一パッケージとして見なしたいもののルートディレクトリに置く、というのが正解でした。
それと、go.modの配置が悪くて依存が解決できない場合、コンパイルエラーのメッセージが"package_name" is not in GOROOTみたいに出ます。(goのバージョンで文言が変わる)なので、このエラーメッセージの時は、go.mod周りを疑うといいのかなと思いました。
何かを調べるときは、英語嫌がらずにまず1次ソースに当たるのがやはりいいですね、、
参考文献
- 投稿日:2020-03-16T10:01:22+09:00
Go 言語でスライスの要素をすべて展開して、インデックスの順序と値を出力する例 ( #go foreach slice all elements )
// https://golang.org/pkg/math/rand/ package main import ( "fmt" ) func main() { cards := []string{"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"} for i, card := range cards { fmt.Println(i, card) } } // e.g // // 0 2 // 1 3 // 2 4 // 3 5 // 4 6 // 5 7 // 6 8 // 7 9 // 8 10 // 9 J // 10 Q // 11 K // 12 AOriginal by Github issue
- 投稿日:2020-03-16T09:47:56+09:00
Go 言語でスライス/配列をランダムな順番でシャッフルっする ( #go shuffle array / slice / cards example )
// https://golang.org/pkg/math/rand/ package main import ( "fmt" "math/rand" "time" ) func main() { cards := []string{"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"} cards4 := []string{} for i := 0; i < 4; i++ { cards4 = append(cards4, cards...) } rand.Seed(time.Now().UnixNano()) rand.Shuffle(len(cards4), func(i, j int) { cards4[i], cards4[j] = cards4[j], cards4[i] }) fmt.Println(cards4) // [7 3 10 7 10 9 A 8 K 9 J 10 10 A K Q K 5 K Q 6 J 3 8 2 6 2 7 Q J 8 3 J 6 2 8 4 9 A 9 2 4 4 3 5 A 4 6 5 Q 7 5] // [5 2 K K K 10 4 J 9 8 A 3 3 5 4 Q 9 3 4 J 7 6 5 Q 10 J A A 2 7 9 6 7 8 8 7 9 K Q 2 J 5 10 2 4 Q 8 A 6 6 10 3] }Original by Github issue
- 投稿日:2020-03-16T09:33:08+09:00
Go 言語で配列(Slice)からランダムに文字列を選ぶ ( #go random select string in slice )
// https://golang.org/pkg/math/rand/ package main import ( "fmt" "math/rand" "time" ) func main() { cards := []string{"A", "B", "C"} rand.Seed(time.Now().UnixNano()) for i := 0; i < 10; i++ { num := rand.Intn(len(cards)) fmt.Println(cards[num]) } } // result // e.g // C // C // C // C // C // B // B // C // B // COriginal by Github issue
- 投稿日:2020-03-16T09:33:07+09:00
Go 言語で スライスを合体させてトランプの組み合わせを作る ( #go join multiple string slice n times )
// https://golang.org/pkg/math/rand/ package main import ( "fmt" ) func main() { cards := []string{"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"} cards4 := []string{} for i := 0; i < 4; i++ { cards4 = append(cards4, cards...) } fmt.Println(len(cards4)) // 52 fmt.Println(cards4) // [2 3 4 5 6 7 8 9 10 J Q K A 2 3 4 5 6 7 8 9 10 J Q K A 2 3 4 5 6 7 8 9 10 J Q K A 2 3 4 5 6 7 8 9 10 J Q K A] }Original by Github issue
- 投稿日:2020-03-16T09:02:25+09:00
Go lang / rand.Seed(time.Now().UnixNano()) used as value / #go
// https://golang.org/pkg/math/rand/ package main import ( "fmt" "math/rand" "time" ) func main() { // NG seed_result := rand.Seed(time.Now().UnixNano()) // OK // Seed does not need return value // rand.Seed(time.Now().UnixNano()) fmt.Println(rand.Int()) }Seed生成した値を使い回すのではなく、一回だけ設定すれば良いみたいだ。なので帰り値は要らない。
Original by Github issue
- 投稿日:2020-03-16T09:02:23+09:00
Go lang で1桁のランダムな数値を生成する / rand.Intn(9) / #go
// https://golang.org/pkg/math/rand/ package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) for i := 0; i < 100; i++ { fmt.Println(rand.Intn(9)) // e.g exclude zero // fmt.Println(1 + rand.Intn(9-1)) } } // Result example // // 2 // 7 // 1 // 0 // 2 // 8 // 3 // 1 // 2 // 4 // 3 // 3 // 3 // 4 // 4 // 7 // 3 // 2 // 6 // 7 // 2 // 4 // 1 // 4 // 4 // 2 // 1 // 2 // 1 // 7 // 1 // 0 // 8 // 1 // 4 // 3 // 5 // 7 // 0 // 7 // 6 // 3 // 8 // 4 // 8 // 4 // 1 // 8 // 2 // 0 // 2 // 4 // 1 // 6 // 7 // 7 // 1 // 1 // 2 // 3 // 5 // 5 // 6 // 5 // 2 // 4 // 5 // 6 // 2 // 4 // 6 // 8 // 2 // 7 // 1 // 3 // 8 // 8 // 6 // 3 // 8 // 0 // 0 // 3 // 1 // 8 // 6 // 1 // 5 // 5 // 1 // 5 // 5 // 1 // 5 // 5 // 2 // 1 // 3 // 7
rand.Intn()
に上限の値を与えれば良いみたいだ。Original by Github issue
- 投稿日:2020-03-16T07:45:26+09:00
【Go】ポインタ
ポインタ
値渡し、ポインタ渡し、参照渡しの違いについて
基本こちらの記事で理解
参照リンク自分なりの整理
# このポインタ型の関数は # アドレス値を受け取る # 自身のアドレス値も別に持ってる("&"でアドレス確認可) # 受け取ったアドレス値にあるデータにアクセス("*") func pointer(i *int){ fmt.Println("値(受け取ったアドレス)=メイン(アドレス)が入る: ", i) fmt.Println("自身のアドレス: ", &i) fmt.Println("値(受け取ったアドレス)のデータにアクセス: ", *i) } # メイン func main() { i := 10 fmt.Println("メイン(値): ", i) fmt.Println("メイン(アドレス): ", &i) # ポインタ型の関数にはアドレスを渡す pointer(&i) }結果
メイン(値): 10 メイン(アドレス): 0xc00009c008 値(受け取ったアドレス)=メイン(アドレス)が入る: 0xc00009c008 自身のアドレス: 0xc000098020 値(受け取ったアドレス)のデータにアクセス: 10