- 投稿日:2019-02-26T22:46:33+09:00
GORM で Null アップデート
はじめに
Go の O/R マッパーである GORM を使っていて、NULL でのアップデートに困っていたので記載しておきます。
環境
- Go 1.11.5
- jinzhu/gorm v1.9.2
結論
以下の User モデルを更新する例です。
type User struct { ID uint `gorm:"primary_key"` Age int Name string } var user User db.First(&user) // 1 カラムの NULL アップデート db.Model(&user).Update("name", gorm.Expr("NULL")) // 実行 SQL : UPDATE `users` SET `name` = NULL WHERE `users`.`id` = '1' // 複数カラムの NULL アップデート db.Model(&user).Updates(map[string]interface{}{ "age": 18, "name": gorm.Expr("NULL"), }) // 実行 SQL : UPDATE `users` SET `age` = '18', `name` = NULL WHERE `users`.`id` = '1'SQL Expression によるアップデートに使用する
gorm.Expr
を使用して、直接NULL
を渡すことで NULL でアップデートができることが分かりました。試行錯誤
通常のアップデートであれば以下のように指定することで可能ですが、NULL でアップデートするには
nil
を渡すなどで上手くいきませんでした。db.Model(&user).Updates(User{Age: 18, Name: "hello"})nil でのアップデート
NULL
を含んだ Insert 文を実行するには、対象のカラムをポインタにしてnil
を渡すことで作成されます。type User struct { ID uint `gorm:"primary_key"` Age int - Name string + Name *string }実際に実行してみると、
NULL
で作成されていることが分かります。user := User{ID: 1, Age: 18, Name: nil} db.Create(&user) // 実行 SQL : INSERT INTO `users` (`id`,`age`,`name`) VALUES ('1','18',NULL)では、同じ要領でアップデートでもポインタにしたカラムに
nil
を渡してみると、無視されてしまうことが分かりました。var user User db.First(&user) db.Model(&user).Updates(User{Age: 18, Name: nil}) // 実行 SQL : UPDATE `users` SET `age` = '18' WHERE `users`.`id` = '1'GORM のドキュメントを見ると以下のように書いてあったので、構造体で実行すると無視されてしまうようです。
When query with struct, GORM will only query with those fields has non-zero value, that means if your field's value is 0, '', false or other zero values, it won't be used to build query conditions,
参考
- 投稿日:2019-02-26T22:19:12+09:00
Docker Registryを構築する
はじめに
本記事は、Docker Registryを使用して、プライベートレジストリ環境を構築する手順について記載しています。
本記事の環境は以下になります。
手元のMacBook Airがクライアントで、iMacがプライベートレジストリ環境になります。Docker Registry
Docker Registryは、Dockerイメージを一元管理するためのリポジトリサービスです。
公式のDocker Hubでは、CentOSなど各種Linuxディストリビューションの公式イメージや、有志で作成されたイメージなどが配布されています。公式イメージの「registry」を使用することで、プライベートレジストリ環境を構築することができます。
現在、registryはVersion2.0系が主流で、Go言語で実装されています。
Docker Registryの構築
イメージのダウンロード(registry)
はじめに、registryイメージをダウンロードします。
- ダウンロードするイメージの確認
docker search registry
NAME DESCRIPTION STARS OFFICIAL AUTOMATED registry The Docker Registry 2.0 implementation for s… 2454 [OK] konradkleine/docker-registry-frontend Browse and modify your Docker registry in a … 213 [OK] hyper/docker-registry-web Web UI, authentication service and event rec… 161 [OK] atcol/docker-registry-ui A web UI for easy private/local Docker Regis… 113 [OK] distribution/registry WARNING: NOT the registry official image!!! … 57 [OK] marvambass/nginx-registry-proxy Docker Registry Reverse Proxy with Basic Aut… 44 [OK] jhipster/jhipster-registry JHipster Registry, based on Netflix Eureka a… 41 [OK] google/docker-registry Docker Registry w/ Google Cloud Storage driv… 37 confluentinc/cp-schema-registry Official Confluent Docker Images for Schema … 29 joxit/docker-registry-ui Docker registry v2 web User Interface 23 [OK] klausmeyer/docker-registry-browser Web Interface for the Docker Registry HTTP A… 17 [OK] openshift/origin-docker-registry The integrated OpenShift V3 registry 13 deis/registry Docker image registry for the Deis open sour… 12 landoop/schema-registry-ui UI for Confluent's Schema Registry 7 [OK] parabuzzle/docker-registry-ui Docker registry frontend for registry v2 6 quiq/docker-registry-ui Docker Registry UI 5 anoxis/registry-cli You can list and delete tags from your priva… 5 [OK] elasticio/docker-registry-ecs Docker image to run Docker private registry … 4 [OK] allingeek/registry A specialization of registry:2 configured fo… 4 [OK] webhippie/registry Docker images for registry 1 [OK] aibaars/docker-registry2-gcs Docker Registry2 w/ Google Cloud Storage dri… 1 yammer/docker-registry-cache Simple docker registry cache using squid-pro… 1 [OK] convox/registry 0 lorieri/registry-ceph Ceph Rados Gateway (and any other S3 compati… 0 torchbox/kube-registry-proxy kube-registry-proxy fork with correct timeou… 0 [OK]
- イメージのダウンロード
docker pull registry
Using default tag: latest latest: Pulling from library/registry 169185f82c45: Pull complete 046e2d030894: Pull complete 188836fddeeb: Pull complete 832744537747: Pull complete 7ceea07e80be: Pull complete Digest: sha256:870474507964d8e7d8c3b53bcfa738e3356d2747a42adad26d0d81ef4479eb1b Status: Downloaded newer image for registry:latest
- イメージの確認
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE registry latest d0eed8dad114 3 weeks ago 25.8MB centos latest 1e1148e4cc2c 2 months ago 202MB
コンテナの起動
次に、ダウンロードしたregistryイメージをもとにレジストリ用のコンテナを起動します。
レジストリは5000番ポートを使用します。
- コンテナの起動
docker run -d -p 5000:5000 registry
e4a6f960fdb3c79250a4baf90e84c96bcebedd86c023900bb3786f32828ae0b8
- コンテナの起動確認
docker ps --format "{{.ID}}\t{{.Image}}\t{{.Ports}}"
e4a6f960fdb3 registry 0.0.0.0:5000->5000/tcp
これでプライベートレジストリ環境が構築できました。
イメージのアップロード
構築したレジストリ環境にアップロードするためのイメージを作成します。
(※)本記事の環境はMac本記事では例として、CentOSののベースイメージにnginxをインストールします。
なお、nginxをインストールするためには、nginx用レポジトリの追加が必要になるため、公式を参考にして、レポジトリファイルの内容をコピーして作成します。
- Dockerfileの作成
vi Dockerfile
# イメージの取得 FROM centos:latest # 作成者情報 MAINTAINER 0.1 https://twitter.com/Brutus08159681 # Nginx用レポジトリファイルのコピー ADD nginx.repo /etc/yum.repos.d/ # Nginxのインストール RUN ["yum", "-y", "install", "nginx"] # Nginxの自動起動設定 RUN ["systemctl", "enable", "nginx.service"]
- nginx.repoの作成
vi nginx.repo
[nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key
- Dockerファイルのビルド
docker build -t webserver .
Sending build context to Docker daemon 10.75kB Step 1/5 : FROM centos:latest ---> 1e1148e4cc2c Step 2/5 : MAINTAINER 0.1 https://twitter.com/Brutus08159681 ---> Running in 4d7ff271e184 Removing intermediate container 4d7ff271e184 ---> 360f621826fd Step 3/5 : ADD nginx.repo /etc/yum.repos.d/ ---> 065d346e93ad Step 4/5 : RUN ["yum", "-y", "install", "nginx"] ---> Running in db772f205c5a Loaded plugins: fastestmirror, ovl //途中省略 Installed: nginx.x86_64 1:1.14.2-1.el7_4.ngx Dependency Installed: make.x86_64 1:3.82-23.el7 openssl.x86_64 1:1.0.2k-16.el7 Complete! Removing intermediate container db772f205c5a ---> a5dc224331c9 Step 5/5 : RUN ["systemctl", "enable", "nginx.service"] ---> Running in cb98e1b1ec8a Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service, pointing to /usr/lib/systemd/system/nginx.service. Removing intermediate container cb98e1b1ec8a ---> afd46670157f Successfully built afd46670157f Successfully tagged webserver:latestプライベートレジストリにアップロードするために、先ほど作成したイメージにタグを付けます。
- タグ付け
docker tag webserver localhost:5000/nginx
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE webserver latest bc4ced76a58a 2 minutes ago 284MB localhost:5000/nginx latest bc4ced76a58a 2 minutes ago 284MB registry latest d0eed8dad114 3 weeks ago 25.8MB centos latest 1e1148e4cc2c 2 months ago 202MB
タグ付けしたイメージをプライベートレジストリ環境にアップロードします。
- イメージのアップロード(コンテナのプライベートレジストリ)
docker push localhost:5000/nginx
The push refers to repository [localhost:5000/nginx] eecb714a1eec: Pushed f12d083c2a5d: Pushed fbf94d353010: Pushed 071d8bd76517: Layer already exists latest: digest: sha256:ec84796f9312457e9afd0a0b3fff1ee40e52512f4f3b83dbaee8a7e728971ee4 size: 1155
イメージのアップロードが完了したので、確認のため、ローカルに保存したイメージを削除します。
docker rmi webserver
Untagged: webserver:latest
docker rmi localhost:5000/nginx
Untagged: localhost:5000/nginx:latest Untagged: localhost:5000/nginx@sha256:ec84796f9312457e9afd0a0b3fff1ee40e52512f4f3b83dbaee8a7e728971ee4 Deleted: sha256:afd46670157fa5608642dae40aaad502eb69787bb0494536d9774089f7e44d8d Deleted: sha256:edfe82b3e83ea6e3fbcb29ba5d801f7a00d5cc6cd1c650fe519d00fbea6a34f7 Deleted: sha256:a5dc224331c9be59615fdf8101d188ed56dd6052176c744f07d1bbdb23109464 Deleted: sha256:313c7c3089ee43faf482383dcb1e04e35cef648c566332239a86920fb4921d1e Deleted: sha256:065d346e93ad13542b6790324f0ca7766335730334d0d39200948239737164a6 Deleted: sha256:d24e00ece851f250e549c84f3e2165f952cee2bf43c913dd1328023e8888bb81 Deleted: sha256:360f621826fd1ad8823e54e70a586a37cbdea1c9844b6b9f7b00853537931548
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE registry latest d0eed8dad114 3 weeks ago 25.8MB centos latest 1e1148e4cc2c 2 months ago 202MB
イメージのダウンロード(プライベートレジストリ環境→nginxのイメージ)
プライベートレジストリ環境にあるレジストリ用のコンテナから、先ほどアップロードしたnginxのイメージをダウンロードします。
- コンテナのプライベートレジストリ環境からダウンロード
docker pull localhost:5000/nginx
latest: Pulling from nginx a02a4930cb5d: Already exists 53cacbc24ea9: Pull complete 6743d7a92897: Pull complete 0b2d1befb17c: Pull complete Digest: sha256:ec84796f9312457e9afd0a0b3fff1ee40e52512f4f3b83dbaee8a7e728971ee4 Status: Downloaded newer image for localhost:5000/nginx:latest
- イメージの確認
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE localhost:5000/nginx latest afd46670157f 3 minutes ago 284MB registry latest d0eed8dad114 3 weeks ago 25.8MB centos latest 1e1148e4cc2c 2 months ago 202MB
- コンテナの起動
docker run --privileged --name "test1" -d -p 8080:80 localhost:5000/nginx /sbin/init
- nginxの起動確認
ブラウザからローカルホストの8080にアクセスします。「Welcome to nginx!」の画面が表示されればOKです。
他のDockerクライアントからのダウンロード
他のDockerがインストールされているクライアントから、プライベートレジストリ環境にあるイメージをダウンロードする手順について解説します。
プライベートレジストリ環境のイメージをダウンロードする場合は、docker pullコマンドの引数にプライベートレジストリ環境のIP:5000/取得イメージ名を指定するだけですが、ホスト側で証明書の設定をしていないと以下のようにエラーが出力されます。
docker pull プライベートレジストリ環境のIP:5000/nginx
Using default tag: latest Error response from daemon: Get https://プライベートレジストリ環境のIPアドレス:5000/v2/: http: server gave HTTP response to HTTPS client
ホスト側で証明書を使用しないでHTTP接続する場合は、以下の設定を行います。
(※)本記事の環境はMacクライアン側のDockerアイコンをクリックして、perferencesのDeamonタブを開きます。
insecure-registries:の「+」をクリックし、プライベートレジストリ環境のIP:5000を追加します。最後に、「Apply & Restart」をクリックします。
Docker再起動後、再度、docker pullコマンドを実行すると、イメージがダウンロードできます。
Using default tag: latest latest: Pulling from nginx a02a4930cb5d: Pull complete 53cacbc24ea9: Pull complete 6743d7a92897: Pull complete 0b2d1befb17c: Pull complete Digest: sha256:ec84796f9312457e9afd0a0b3fff1ee40e52512f4f3b83dbaee8a7e728971ee4 Status: Downloaded newer image for プライベートレジストリ環境のIPアドレス:5000/nginx:latest
おわりに
プライベートレジストリ環境を構築することで、統一したイメージを使用した開発ができるので、Dockerの持ち味をより引き出せます。
参考
- 投稿日:2019-02-26T19:55:32+09:00
WordPress 4.7 REST APIの時刻フォーマットがイケてないのでGolangで苦労した話
概要
- wordpress 4.7にはREST APIで記事の一覧や内容を取得できる機能があります
- これを利用してgolangで記事の一覧を取得するクライアントを作成したが時刻のパースでエラーが発生します
- 何故かとおもったらAPIの返す時刻のフォーマットがRFC3339「もどき」でGoがパース不可能になっていました
wordpress4.7のREST APIについて
- REST APIで返ってくる値の形式は以下のようになっています
こちらのページのjsonを引用させてもらうと以下のようになります
[ { "id": 14687, "date": "2017-01-12T12:00:30", "date_gmt": "2017-01-12T03:00:30", "guid": { "rendered": "http://loumo.jp/?p=14687" }, "modified": "2017-01-07T19:13:26", "modified_gmt": "2017-01-07T10:13:26",略
Goでパースできない
上記のようなjsonをパースするときGoでは以下のようにstructを宣言してパースを行いますが
type News struct { Date time.Time `json:"date"` DateGmt time.Time `json:"date_gmt"` } var newsResponse []*News decoder := json.NewDecoder(res.Body) // APIから取得してきます if err := decoder.Decode(&newsResponse); err != nil { fmt.Println(err) return nil, err }このときこのようなエラーが出ます
parsing time ""2019-02-26T02:04:34"" as ""2006-01-02T15:04:05Z07:00"": cannot parse """ as "Z07:00"なぜパースができないのか
- GoはRFC3339に則ってパースしようとしますが、Wordpressの吐いている時刻のフォーマットが正しいRFC3339ではないことが原因のようです
"date": "2017-01-12T12:00:30"
- 正しくは
"2017-01-12T12:00:30Z"
または"2017-01-12T12:00:30+0900"
などでなければいけません- ということでWordpressのREST APIの時刻をGolangで扱うときは気をつけましょう....
- 投稿日:2019-02-26T19:44:06+09:00
【Udemy】gRPC [Golang] Master Class: Build Modern API & Microservicesをやってみた
はじめに
- 年末にUdemyのコースがセールになってまして、Go言語使っててHTTP/2やmicroserviceが学べるコースがないかなと探してたらこれが見つかり買ってみました。
gRPC [Golang] Master Class: Build Modern API & Microservices- でもすぐにやらなくて積んdemyなってました。。。
- なので【Qiita×Udemy特別企画】学習応援イベントがやってたので試しにのっかってみました。
コース概要
の前に
英語
このコースは中身をみればわかると思いますが、全部英語です。
ですが大して難しい英語を使ってるわけでもありませんし、プログラミングですからソースコード見て理解すればいいので英語がハードルになることはそんなに無いかと思います。
自分はまあまあ英語が得意なんで苦手な方はいくらか差し引いて考えた方がいいかも。概要
ざっとこんな感じでした。
- gRPC オーバービュー
- gRPC 詳細
- gRPC の各種実装パターン
- Unary
- Server Streaming
- Client Streaming
- Bi-Directional Streaming
- その他機能の実装
- エラー実装
- デッドライン
- SSLセキュリティ
- 別言語からの通信(Java)
- gRPC のEvans クライアントの使い方
- 実践的なアプリを作ってみる
- MongoDBと連携してCRUD APIを実装
- おまけ(クーポンとか)
gRPC の各種実装パターンでそれぞれUnary、Server Streaming、Client Streaming、Bi-Directional Streamingを説明していて、そこについてのプログラミング解説が主な内容でした。
やったこと
ひたすら写経!Webページを作った時のように見栄えの良いものを作るわけでもないので、かっこいいアウトプットはありません。
リポジトリを載せておきます。
https://github.com/suganoo/go-grpc-coursegRPC のサーバーを起動して、クライアントを実行してみるのをひたすらやってました。
学んだこと
ざっくり各種パターン
Unaryパターン
単純にリクエストを投げて、レスポンスを受けるというパターンです。(1:1)
REST APIみたいなものでした。ex. 2つの数字を送ったら合計値が返される。Server Streaming
1回リクエストを投げて、複数のレスポンスを受けるパターンです。(1:n)
ex. ある数値を送ったら素数がすべて返ってくる。Client Streaming
複数回リクエストを投げたら、1回のレスポンスを受けるパターンです。(n:1)
ex. 複数個の数値を送ったら、平均値が返される。Bi-Directional Streaming
相互に複数回通信するパターンです。(n:n)
ex. 複数個の数値を用意しておき、順次1コづつ数値を送ると、それまでに受けた数値の最大値を更新した時に最大値を返す。Calculator
上で例として書いてますが、平均値を返したり、最大値を返したりするアプリを作ってみました。なるほどという感じです。
MongoDBと連携してアプリ
ブログを作ったと想定して、MongoDBにデータを入れこみCRUDを実装しました。MongoDBは実際使うのは初めてで、これはなかなかMongoDBも含めて勉強になりました。便利だねー
Evans CLI
gRPCのサーバーに対してのクライアントアプリです。最後にちょろっと出てきてこいつは便利だなー!と思いました。でもDLしてビルドしたところなぜか接続できず残念でした。。。
https://github.com/ktr0731/evans別言語からの通信(Java)
これは動画からしか見てませんが、他の言語からちゃんと通信できてて便利でした。
MongoDBのドライバ設定はちょっと注意
バージョンが違うのかimportの定義の仕方は下記のような書き方でないとエラーになってました。
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options"時間があればやりたかったこと
Udemy特別企画の締め切りを目標にしてて、さらに始めるのが2月半ばを過ぎてからだったので、コースを終わらすことしかできませんでした。
コーディングしてるといろいろとやってみたいことが出てきたので、それが今後の宿題かなと思います。
- HTTP/2通信としてはどうなってるのか。 サーバーはnet.Listen(...)で始めてるし、クライアントはgrpc.Dial(...)で始めてたのでヘッダーとかどうなってるのか。
- protoc で生成されるGoのソースを見てみる。
- TLS通信の仕方。各種サーティフィケーションファイルはコマンドコピペだったのでちゃんと復習したい。
- gRPC 使ってチャットアプリ作ってみたいですね。
最後
やっぱこういう企画がないとやらないですね(汗)。
ですが今回を気にgRPCが学べたのでとてもよかったです。
またUdemyは年末年始になるとセールになるのでお得です。
Udemyだと新しい技術が出てきたときに英語版であればいち早くコースが出てくるのでチェックしてみるといいかと思います。
- 投稿日:2019-02-26T19:23:49+09:00
Go1.12リリースしたのにまだReplace()に-1渡してるの?静的解析でReplaceAllに切り替えれるコードを検知する静的解析ツールを作った
Go1.12がリリースしました!!尊い!!尊い!!
Go1.12 から入る ReplaceAllメソッドはご存知でしょうか。リリース記念に ReplaceAll に変更可能なコードを検知する簡単な静的解析ツールを作ったので、ReplaceAllの紹介 & ツールの紹介も兼ねてどうやって作ったのかも軽く紹介します。
そもそも ReplaceAll って ??
今まで指定した文字列やbyte列をreplaceする際は下記のようにReplaceメソッドを使っていました。
// stringsパッケージの場合 func Replace(s, old, new string, n int) stringn<0の値を渡せばoldで指定した箇所全てをnewに変換できます。しかし、Go1.12からReplaceAllが使えます。
// stringsパッケージの場合 func ReplaceAll(s, old, new string) stringreplaceAll は bytes と strings パッケージに搭載されてます。Replaceに-1とか渡さなくともこっち使えば良いですね。
作った静的解析ツール
インストールして
$ go get github.com/po3rin/chkreplace/cmd/chkreplace使う
$ chkreplace github.com/po3rin/chkreplace/example example/replace.go:11:17: detect strings.Replace use negative value -1 example/replace.go:23:15: detect bytes.Replace use negative value -1検知できてます。
どうやって作ったのか
最近流行りの analysisパッケージで作ってます。Goで静的解析ツールを作成するための仕組みを提供するパッケージです。
https://godoc.org/golang.org/x/tools/go/analysis出だしは @tenntenn さん作の skeleten という静的解析ツールのテンプレートを生成してくれるツールを使いました。雛形も作ってくれるので、そのまま静的解析の作業に入れます。
analysisパッケージについての解説は強強な方達が詳しい解説を書いているのでそちらに譲ります。analysisパッケージを触ったことない方はこちらの記事が最高に勉強になります。
analysisパッケージの基本的な使い方の記事
https://budougumi0617.github.io/2019/02/01/how-to-use-analisys-package/実際にfindcallというパッケージの実装を見ていく記事
https://tech.mercari.com/entry/2018/12/16/150000実際のコードを少し解説
こちらが今回作ったchkreplaceの主要コード箇所です。skeletenから生成したinspect.Preorderに実際の処理を書いていきます。ゴリ押し感が否めない。。コメントで軽く解説します。
inspect.Preorder(nodeFilter, func(n ast.Node) { // ast.CallExpr という 関数またはメソッド呼び出しに関する構文木のノードをcallとして所得 if call, ok := n.(*ast.CallExpr); ok { var id *ast.SelectorExpr var idx *ast.Ident switch fun := call.Fun.(type) { case *ast.SelectorExpr: id = fun default: return } switch x := id.X.(type) { case *ast.Ident: idx = x default: return } if id != nil && idx != nil && !pass.TypesInfo.Types[id].IsType() { // strings.Replace もしくは bytes.Replace であることを確認 if id.Sel.Name == "Replace" && (idx.Name == "strings" || idx.Name == "bytes") { var expr *ast.UnaryExpr // 念の為引数の数が4つであることを確認 if len(call.Args) != 4 { return } // 4つ目の引数の構文木ノードを所得 switch arg := call.Args[3].(type) { case *ast.UnaryExpr: expr = arg default: return } switch a := expr.X.(type) { case *ast.BasicLit: // 引数がintかつマイナスの値かどうかをチェック。 // tokenパッケージに定義されているものものと付き合わせます if a.Kind == token.INT && expr.Op == token.SUB { // 検知結果をファイルパス、行数と一緒に出力!!!! pass.Reportf(call.Lparen, "detect %s.%s use negative value -%s", idx.Name, id.Sel.Name, a.Value) } } } } } })今回は findcall パッケージの実装を大いに参考にしています。
https://github.com/golang/tools/tree/master/go/analysis/passes/findcall実際に構文解析するフェーズにおいてはGoのASTの知識が必要です。全てのノードを知っておくなんてのは無理なので、最初はast.PrintなどでテストケースのASTを確認しておくと良いでしょう。僕はそこからゴリゴリとswitch文で書いていきました。構文解析においては下記が良解説すぎるので一読をお勧めします。
https://motemen.github.io/go-for-go-book/テストの書き方
先ほど上に記載したコードの出力部分はこうなっています。
pass.Reportf(call.Lparen, "detect %s.%s use negative value -%s", idx.Name, id.Sel.Name, a.Value)skeltonで生成した
testdata/src/a/a.go
に解析対象のコードを書き、解析時に出力される文字列をwantをつけてコメントとして書いておけばテストは完成です。つよ!!func stringsReplaceUseNegative() { text := "aaabbbcccaaabbbccc" strings.Replace(text, "aaa", "eee", -1) // want "detect strings.Replace use negative value -1" fmt.Println(text) } func stringsReplacePositive() { text := "aaabbbcccaaabbbccc" strings.Replace(text, "aaa", "eee", 2) fmt.Println(text) } func bytesReplaceUseNegative() { b := []byte("aaabbbcccaaabbbccc") bytes.Replace(b, []byte("aaa"), []byte("eee"), -1) // want "detect bytes.Replace use negative value -1" fmt.Println(b) } func bytesReplacePositive() { b := []byte("aaabbbcccaaabbbccc") bytes.Replace(b, []byte("aaa"), []byte("eee"), 3) fmt.Println(b) }注意点
chkreplace はまだ strings や bytes という文字列ではないと検知しないので下記のようなimportをしていると検知できません。。
import ( s "strings" )その為、パッケージを解析する必要がありますが、力尽きました。。。すいません。。
まとめ
analysisパッケージのおかげで簡単に静的解析ツールを作成できました。初めて作ったのでゴリゴリ感が否めないですが、静的解析は楽しかったです。もっといい書き方あるよ!ってのがあれば教えてください!!
ちなみにこの画像は https://qiita.com/po3rin/items/eac851304cf058c532af で紹介したツールを使って生成してます。ぜひ投稿する際に使ってみてください!!
- 投稿日:2019-02-26T17:00:00+09:00
Go の http request 中に発生した本来のエラーを取得する!
例えばこんな感じのコードを皆さんはよく書くはずです。
ctx, cancel := context.WithCancel(context.Background()) req, _ := http.NewRequest("GET", url, nil) resp, err := http.DefaultClient.Do(req.WithContext(ctx)) if err != nil { return err }今回はこの
return err
で受け取った error の値が下記のようになる事がありました。Get https://example.com: context canceledどうしても
context canceled
のエラーだけを取得して、何かしらのアクションをしたかったのでずっとこんなコードを書いてました。func errCheck(err error) error { msg := err.Error() if strings.Contains(msg, context.Canceled.Error()) { // do something return nil } return err }しかし上記のコードはなんか気持ち悪かったので、http パッケージのソースコードを読んだ所、下記のようにハンドリングできることがわかりました。
func errorCheck(err error) error { if v, ok := err.(*url.Error); ok { err = v.Err if err == context.Canceled { // do something return nil } } return err }どうやらリクエスト内で発生したエラーは何故か
*url.Error
にラップされるらしいですよ!
- 投稿日:2019-02-26T16:09:23+09:00
GolangのsqlxでIN条件を扱う
golangにはsqlxという
database/sql
の拡張機能を提供するライブラリがあります。SQLを書いていると、WHERE句に
IN
という条件を使いたくなることがありますが、sqlxでは普通のやり方ではこのIN
が利用できません。sql := `SELECT * FROM users WHERE id IN (?)` var users []User if err := sqlx.Select(&users, sql, []int{1,2,3,4,5}); err != nil { // これはエラーになる }sqlxはこの問題を解決するために、
In(query string, args ...interface{}) (string, []interface{}, error)
というヘルパ関数を提供しています。
この関数を利用することでIN
条件を持つクエリを書くことが出来るようになります。sql := `SELECT * FROM users WHERE id IN (?)` sql, params, err := sqlx.In(sql, []int{1,2,3,4,5}) if err != nil { log.Fatal(err) } // sql, paramsは以下のような形で返され、有効なクエリを実行することが出来るようになります。 // // sql: "SELECT * FROM users WHERE id IN (?, ?, ?, ?, ?)" // params: [1 2 3 4 5] var users []User if err := sqlx.Select(&users, sql, params...); err != nil { log.Fatal(err) }
- 投稿日:2019-02-26T16:09:23+09:00
GolanのsqlxでIN条件を扱う
golangにはsqlxという
database/sql
の拡張機能を提供するライブラリがあります。SQLを書いていると、WHERE句に
IN
という条件を使いたくなることがありますが、sqlxでは普通のやり方ではこのIN
が利用できません。sql := `SELECT * FROM users WHERE id IN (?)` var users []User if err := sqlx.Select(&users, sql, []int{1,2,3,4,5}); err != nil { // これはエラーになる }sqlxはこの問題を解決するために、
In(query string, args ...interface{}) (string, []interface{}, error)
というヘルパ関数を提供しています。
この関数を利用することでIN
条件を持つクエリを書くことが出来るようになります。sql := `SELECT * FROM users WHERE id IN (?)` sql, params, err := sqlx.In(sql, []int{1,2,3,4,5}) if err != nil { log.Fatal(err) } // sql, paramsは以下のような形で返され、有効なクエリを実行することが出来るようになります。 // // sql: "SELECT * FROM users WHERE id IN (?, ?, ?, ?, ?)" // params: [1 2 3 4 5] var users []User if err := sqlx.Select(&users, sql, params...); err != nil { log.Fatal(err) }
- 投稿日:2019-02-26T16:07:44+09:00
Go言語のHugoを使おうとして、ハマったので備忘録
HUGOとは?
静的なHTMLを作成することができるGo言語製の静的ページジェネレーターらしいです。
インストール環境
go version go1.11.4 linux/amd64公式サイトを参考に
公式サイトに
mkdir $HOME/src cd $HOME/src git clone https://github.com/gohugoio/hugo.git cd hugo go install --tags extended上記を試したのですが、依存関係のパッケージが無いと怒られました。(すみませんエラー残し特の忘れたので、多分パッケージ関連のエラーだったと思われます)
govendorを試す
govendor get github.com/gohugoio/hugoを試したのですが、
github.com/gohugoio/hugo/vendor/github.com/cpuguy83/go-md2man/md2man vendor/github.com/cpuguy83/go-md2man/md2man/roff.go:15:15: undefined: blackfriday.Extensionsというエラーに、
そのままググったところ、
https://github.com/gohugoio/hugo/issues/4390
こちらに行き着きました。mageを使う
govendor ではなく、mage というものを使えとのことでしたので、
go get github.com/magefile/mage go get -d github.com/gohugoio/hugo cd $HOME/go/src/github.com/gohugoio/hugo mage vendor mage installで、インストール出来ました。
なんかモヤモヤするけど。何か、誤り等ありました。ご指摘いただけると幸いです。
- 投稿日:2019-02-26T16:03:06+09:00
Go言語を学習し始めて、簡単なCRUDアプリをクリーンアーキテクチャで作成するまで②
このシリーズ目次
- Go言語を学習し始めて、簡単なCRUDアプリをクリーンアーキテクチャで作成するまで① - Qiita
- Go言語を学習し始めて、簡単なCRUDアプリをクリーンアーキテクチャで作成するまで②←今ココ
- Go言語を学習し始めて、簡単なCRUDアプリをクリーンアーキテクチャで作成するまで③ - Qiita
はじめに
こちらの記事は前回Go言語を学習し始めて、簡単なCRUDアプリをクリーンアーキテクチャで作成するまで① - Qiitaの続きとなります。
Go言語を学習し始めて、簡単なCRUDアプリをクリーンアーキテクチャで作成するまで②|Wano Group Developers Blogにあげたものと同じものになります。しかしこの記事から読まれても全く支障がないので、お好きに読まれてください!
とりあえず文法が一通り抑えられたので、チームの先輩shunさんのアドバイスで、Goで簡単なCRUDアプリ(Web)作っていくことにしました。
とりあえず4段階くらいに分けるかあと思い、4ステップに分けて順次進めていきました。②簡単なCRUDのWebアプリケーション作成
1.まずgoenvとdep導入
Gopkg.toml · dep
Golangをgoenvを使ってインストールしてみた - Qiita
goenvでgoをインストール 〜初心者向け〜 - Qiita
依存関係管理ツールdep(golang) - Qiitagoenvはgoのバージョン管理ツールで、depはパッケージ管理ツールです。どちらも私が配属されたVideo Kicksチームで採用しているということで使ってみました。
rubyでいうrbenvとBundlerということで、ここは問題なく導入。2.echoでRESTAPI作る
【go】echoでapiサーバーを実装するときに最低限必要そうなことをまとめておく - とりあえずphpとか
Go の echo ってWebサーバーでサクッと REST しちゃう - QiitaEchoというのは、Goでよく使われるフレームワークです。
うちのチームも採用しているようなので使ってみました。
API用途が多い、軽量のフレームワークになります。だいたい公式ドキュメントEcho - High performance, minimalist Go web frameworkに使い方が書いてあるので、そこメインで参考にしました。わかりずらいところは上記サイト参照。
まずはJSON返す形で組んで動かして、echoの理解を深めました。3.DB導入 Gorm使い方覚える
Mac へ MySQL を Homebrew でインストールする手順 - Qiita
GoでMySQLに接続する - Qiita
【GORM】Go言語でORM触ってみた - Qiita
GoのORマッパーでJoinデータのとり方を学ぶ! - イノベーション エンジニアブログGormとは、Goでよく使われるORMです。
僕はフレームワークはRORしか使ったことがなかったので、別でORMを使っていくのは初めてでした。
でも結構使い安くて、公式ドキュメントGORM - The fantastic ORM library for Golang, aims to be developer friendly.もわかりやすいのである程度基本的なお作法はすぐ抑えやすいと思います。
前の記事で言及したpreload問題などなど、ORMあるあるかもなやばいSQL発行してるみたいなところは、業務で使いながら適切なクエリチューニングができるようにしていきます。4.CRUDアプリ作成
Go言語のWebフレームワーク「Echo」を使ってみる ①(Hello World的な) - Qiita
Go言語のWebフレームワーク「Echo」を使ってみる ②(リクエストパラメータの扱い) - Qiita
Go言語 - HTMLテンプレートの使い方 - 覚えたら書く
golang : template の range で index を使う - i++1~3まで行えていれば、あとはwebのview側どうするか、ってとこだけだと思います。そこでechoのTemplates | Echo - High performance, minimalist Go web frameworkを利用して、renderingしていくのがメジャーのようです。
RORとかのフルスタックフレームワークにおんぶに抱っこだったのでわりかし使いづらさを感じましたが、こちらも公式リファレンスが一番わかりやすい気がします。その他細かいとこ(template内でのスライスの回し方とか)は上記のサイト参考にしながら進めました。そうしてざっくり作ったのがこちらになります。
GitHub - takafk9/echo_crud細かいエラーハンドリングとかログ管理とか一切してないです。とりあえずお試しで一通りいじってみた感じです。
突っ込みどころばかりだと思うのですが、0からとりあえず簡単な仕様を作ってみたい方向けということで。(許して)Goはhtmlレスポンス拡張put,delete対応してないので、下記のサイトを参考に実装しようと思ったのですが、うまくいかず保留中。
[go,echo]formからputをリクエスト~methodOverRide~ - Qiita@y_ussieさんの実装Go言語のWebフレームワーク「Echo」を使ってみる ②(リクエストパラメータの扱い) - Qiitaをかなり参考にしました。(大感謝)
複数画面ある場合どうtemplate扱おうかしら、って思ってたんですが、@y_ussieさんのmapにtemplate入れるの案良いな、と思って採用していました。
main.go@y_ussie// Render はHTMLテンプレートにデータを埋め込んだ結果をWriterに書き込みます。 func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { return templates[name].ExecuteTemplate(w, "layout.html", data) } // 初期化を行います。 func init() { loadTemplates() } // 各HTMLテンプレートに共通レイアウトを適用した結果を保存します(初期化時に実行)。 func loadTemplates() { var baseTemplate = "templates/layout.html" templates = make(map[string]*template.Template) templates["hello"] = template.Must( template.ParseFiles(baseTemplate, "templates/hello.html")) templates["hello_form"] = template.Must( template.ParseFiles(baseTemplate, "templates/hello_form.html")) }ただ、initの処理で画面数だけtemplate初期化して用意しておく必要はないかなと思ったので、以下のようにgetTemplateメソッド作って、nameに対応したtemplateをBaseTemplateに都度マージして、返すようにしました。
main.go@fukubaka0825// Render はHTMLテンプレートにデータを埋め込んだ結果をWriterに書き込みます。 func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { return getTemplate(name).ExecuteTemplate(w, "layout.html", data) } func getTemplate(name string)(*template.Template){ switch name { case "posts_all": return template.Must(template.ParseFiles(_const.BaseTemplate, _const.PostsAll)) case "update_post_form": return template.Must(template.ParseFiles(_const.BaseTemplate, _const.UpdatePost)) default: return nil } }
※2019/2/26 訂正
指摘があったため訂正です。// template情報をキャッシュ var templates = template.Must(template.ParseFiles("edit.html", "view.html"))上記の例のように、template.Mustでパース結果をキャッシュして、その結果をMapに詰めて
取り出しやすくするのは非常に理にかなっているので、@y_ussieさんの実装のままの方が良いみたいです。
ここで勉強できてよかった!
ディレクトリ分けとしてはオーソドックスなMVCな感じで分けました。
ただ今の時流でGoでコテコテのMVCって流石に、、、ってなってうちのPM(VPoE)に相談したところ、
「クリーンアーキテクチャで作り変えて見れば?軽くテストまで書いてみると恩恵も理解できて良いと思うよ」
とアドバイスをいただいたので、作り変えてみることに。
いやでもしかし、内心 「クリーンアーキテクチャって聞いたことあるけどなんぞ???」 って感じで、、、、、、、
ここから③に続きます。明日か明後日にあげれると思います。ではまた次回!!!!
- 投稿日:2019-02-26T11:04:40+09:00
mercarigo スライドまとめ(個人用)
はじめに
参加してきて非常にためになったので個人的な復習用に
そういえば資料は数日後にまとめて会社のブログに投稿します。 #mercarigo
— morikuni (@inukirom) February 25, 2019無い部分はまとめて頂けるようなので、後で追記します。
JavaとGoの比較
登壇者:@kentan
twitter: https://twitter.com/kentandx
スライド掲載なしyamlをテストする
登壇者:@babarot
twitter: http://twitter.com/b4b4r07
Testing with YAML - Speaker Deckshallow copyに気をつけろ
登壇者:@t-naritomi
twitter: https://twitter.com/ques0942
sliceのコピーで起きた不思議な話 - Google スライドコンパイラ書いた話
登壇者:@kitasuke
twitter:https://twitter.com/kitasuke
Writing a compiler in goコンパイラ書いた話(mapとheap)
登壇者:@dqneo
twitter: http://twitter.com/dqneo
Goのmapとheapを自作してみた / How to create your own map and heap in Go - Speaker Deckポメラニアン専用Goコンパイラ
登壇者:@tenntenn
twitter: http://twitter.com/tenntenn
ポメラニアン専用Goコンパイラ - Google スライドGoのエラーハンドリング
登壇者:@morikuni
twitter: http://twitter.com/inukirom
mercari.go #6 - Google スライド
- 投稿日:2019-02-26T08:32:13+09:00
(時間がない人向け) プログラミング言語Go 1章 まとめ
はじめに
The Go Programming Language の1章を読んで、理解したこと、気になったことを時間がなくて読めない人向けにまとめます。 (日本語訳の書籍はこちら プログラミング言語Go )
第1章 チュートリアル
1.1 Hello World
- Goはコンパイル言語。
- Goのツールチェーンはソースプログラムとそのプログラムが依存しているものを機械語に翻訳。
- サブコマンドの中で最も単純なものが
run
でファイル名が.go
で終わる1個以上のファイルをコンパイルし、ライブラリとリンクし、その結果さくせいされた実行可能ファイルを実行する。- Goのコードはパッケージで構成される。
- 1つのパッケージは単一ディレクトリないの1個以上の.goファイルから構成される。
- 各ソースはpackage宣言で始まる。
- mainパッケージは特別で、ライブラリではなく単独で動作する実行可能なプログラムを定義。
- mainパッケージ内ではmain関数も特別で、この関数からプログラムが実行される。
- import文でどのパッケージが必要か伝える。
- import宣言はpackage宣言のあとである必要がある。
- 同じ行に2つ以上の文や宣言がないかぎり、セミコロン
;
は不要。gofmt
ツールはコードを標準書式に書き換える。- goツールのfmtサブコマンドは指定されたパッケージの全てファイルや、デフォルトでは現在のディレクトリにある全てのファイルに対して、gofmtを適用。
1.2 コマンドライン引数
- osパッケージはオペレーティングシステムを扱う数値と値を提供。
- コマンドライン引数はArgsという変数で利用できる(os.Args)
- Goではすべてのインデックスの指定は半開区間。
- コメントは
//
からその行の終わりまで。- 慣習によりパッケージ宣言の直前のコメントにパッケージの説明を記述。
- mainパッケージに対しては、プログラム全体を説明する1行か数行からなる完結した文を記述。
- 変数が明示的に初期化されない時はゼロ値に初期化される。
:=
記号は省略変数宣言の一部。- forループはGoで唯一のループ文。
- Goは使われない変数を許さずコンパイルエラーになる。
解決策は名前が_
であるブランク識別子を使う。1.3 重複した行を見つける
fmt.Printf
関数は複数の式のリストからフォーマットされた出力を生成。 Printfはverb
(ヴァーブ, 動詞) と呼ぶこのような変換を10数個以上持っている。%d 10進表記の整数 %x, %o , %b 16進表記, 8進表記, 2進表記 %f, %g, %e 浮動小数点 : 3.141593 3.141592653589793 3.141593e+00 %t ブーリアン (true or false) %c ルーン %s 文字列 %q 引用符で囲まれた文字列"abc", あるいは、 ルーン 'c' %v 任意の値に対する自然なフォーマット %T 任意の値の型 %% パーセント記号そのもの
- 文字列リテラルは、そのままでは不可視な文字列を表現するために、 タブ
\t
や 改行\n
のようなエスケープシーケンスを含むことができる。- 慣習的に
f
で名前が終わるフォーマット関数はfmt.Printfのフォーマット規則に従う。
一方で、名前がln
で終わるフォーマット関数はPrintlnの規則に従い、
引数が%v
でフォーマットされ、その後に改行が続く。1.4 GIFアニメーション
- リサジュー図形 (Lissajous figure)
- コンポジットリテラル。
1.5 URLからの取得
- ネットワーク通信を行うパッケージはをnetの下にまとめている。
- http.GetはHTTPリクエストを発行し、エラーがなければレスポンス構造体respで結果を返す。
1.6 URLからの並行な取得
- Goの最も興味深く斬新な特徴は1つは並行プログラミングのサポート。
- ゴルーチン(gorutine)は関数の並行した実行。
- チャネル(channel)はゴルーチンが特定の型の値を他のゴルーチンへ渡すことを可能にする。
- main関数は1つのゴルーチンで動作し、go文は新たなゴルーチンを生成。
- make関数をつかってチャネルを作成。
1.7 ウェブサーバー
- スラッシュで終わっているハンドラのパターンは、そのパターンを接頭辞として持つ全てのURLに一致。
- .Lock()と.Unlock()を用いて、一度に一つのゴルーチンだけがその変数へ。 アクセスすることを保証し、Race condition(競合状態)を回避。
- 関数リテラル = 使われる時点で定義される無名関数。
1.8 残りの項目
- switch文のdefaultの位置はどこでも良い。
- switch文のcaseはC言語のようにcaseは次のcaseへと通り抜けていくことはない。
ほとんど使われないが、この動作を無効にできる、 faillthrough がある。- switch文ではオペランドを必要としない場合がある(ブーリアン式であるようなケース)
この形式は タグなしswitch文(tagless switch)と呼ばれswitch trueと同じ。- switch文はforやifと同様に、省略可能な単文(省略変数宣言、インクリメント文、代入文、関数呼び出し)を含んでも良い。
- 文にラベルをつけることができるので、breakとcontinueはそれらのラベルを参照することもできる。
- goto文もあるが、これは自動生成されたコードで使われることを意図しており、プログラマが普通に使うことは意図していない。
- type宣言で既存の型に対して名前をつけることができる。
- Goは変数のアドレス値であるポインタを明示的に扱える。
&
演算子は変数のアドレスを生成し、*
演算子はポインタが参照している変数を取り出す。
しかし、ポインタへの演算はない。- メソッドは名前付き型に関連づけられた関数。
- インターフェースは異なる具象型(concreate type)を同じように取り扱うことを可能にしてくれる抽象型(abstract type)。その取り扱いは、具象型がどのような表現あるいは実装をされているかではなく、どのメソッドを持っているかに基づく。
- コメントは
//
以降か/* */
に囲われた物。