毎週テキサス・ホールデムをやってエンジニア力を鍛える弊社の紹介
これはCluster,Inc. Advent Calendar 2017の第10日目の記事です。
弊社の会社紹介しようかなと思います。 弊社VRの会社なんですが、社内では毎週テキサス・ホールデムのポーカーをやっています。(全然VR関係ないです 😇)
IVSのピッチコンテストLaunch Padというイベントで優勝して 本当に叶うAmazonウィッシュリスト というのがAmazonさんより贈呈されて、僕がポーカーやりたいなーと思ってポーカーテーブルをいれたらめっちゃ社内で流行って大満足という感じです 😄
他にもボドゲがいっぱい
IVS LaunchPad優勝賞品の「本当に叶うAmazonウィッシュリスト」を使ってAmazonさんに願いを叶えていただきました。弊社エンジニア陣のボドゲ愛が色濃く反映された結果がこちらです。 pic.twitter.com/iqzfdDLFEc
— Tsukasa (@tsukapon127) 2017年8月28日
これがIVSのイベントの記事です
テキサス・ホールデムとは?
テキサス・ホールデム(Texas hold 'em)はポーカーの一種。各プレイヤーごとに配られる2枚の手札と、コミュニティ・カードと呼ばれる全プレイヤー共通のカード(最大5枚)を組み合わせてプレーする。アメリカ合衆国のカジノにおいては最もポピュラーなゲームのひとつである。通常は2人から10人で行われる。
社内の様子など
- 大体週1で開催されてる(毎週木曜の20:00くらいから23:00くらいまでやってる)
- 前職でお世話になった@axrossさんが外部講師(?)として時々きてくれる(結構頻度高)
- 社外の人も口コミというか、社員の友人的な感じで参加して結構ワイワイやってる
- ポーカーは論理や確率に基づいたプレイが大事で、ポーカーが上手い人はエンジニアとしての仕事の進め方とか判断力を鍛えられている気がする(そんな気がする)
- 主に上達目的でリングゲームをやっているがたまに気分転換にトーナメントをやったりしてる
- ポーカー道あたりで用語を抑えたあとにフィル・ゴードン本(緑)を買って読むメンバーが結構多い(そろそろ会社の本棚に置いてもいいのでは感)あとはひたすら遊んでる
- 社員13人中9人くらいポーカーやっててVRの会社のはずなんだけど、ポーカーの会社みたいになってる現状
フィル・ゴードンのポーカー攻略法 入門編 カジノブックシリーズ
- 作者: フィル・ゴードン
- 出版社/メーカー: パンローリング株式会社
- 発売日: 2013/03/17
- メディア: Kindle版
- この商品を含むブログを見る
弊社でポーカーで遊びたーいという方がいたら気軽にお声がけください!
現場で役立つシステム設計の原則を読んだ
ざっくり説明すると、DDDの文脈にあるドメインモデルやドメインオブジェクトとかを意識してプログラミングとかやっていきましょうという感じ。
エリック・エヴァンスのDDD本は概念的な話やユビキタス言語とかをちゃんと定義してドメインモデルを作っていってチーム全員でやっていき!って感じで、 なんか手っ取り早くいい感じのアーキテクチャとかプログラミングでパワーアップして、いい感じのプロダクトを作りたい!!!! って人には難しい内容となっていたと思います。
(DDD本、自分はとてもいい本だと思ってますので、あとで読むのはおすすめします)
現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法
- 作者: 増田亨
- 出版社/メーカー: 技術評論社
- 発売日: 2017/07/05
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
本書はDDD本と違って、プログラミングからのアプローチでDDDの一部を徐々に学んでいく感じがしました。
他にもオブジェクト指向やWebAPI設計やDB設計やリファクタリングなど、システム開発で必要となる基本的な知識を様々な良書から引用して、実際に開発するときに役立つ形でまとまっていました。
正直、 数年前に読みたかった... という一冊です。(引用元の本はほとんど読んでいたので)
システム開発する前にたくさん読む本がある〜という人向けに一冊である程度必要な知識を得ることができてとても良いと感じました。 理解できなかったところやもうちょっと深掘りしたいと思ったものは、書籍に引用元の本がリンクされているのでそれを辿っていくと良さそうです。
いきなりDDDとか言われても... みたいな人にも優しい内容になっていて、とりあえずこの本の内容を理解して開発していればいずれ「DDDやるぞ!」ってなったときに割りとスムーズにやっていけそうだなと思いました。
goa勉強会 in 六本木一丁目でLTしてきましたので内容の補足など
LTしてきました。色々学びあって楽しかったです。
会場提供のistyleさんありがとうございました。
少しだけ補足説明とexampleコードを書いたので紹介しようと思います。
LTした資料
注意点
- goとgoaについてすでにある程度理解がある人向けです
- goaのversionは、v1.2.0の前提で話しています
go generate使ってコード生成して開発する流れについて
–forceをつけることで毎回mainのgenerateは作り直してます。
//go:generate goagen -o gen main --force -d github.com/kyokomi/example_goa_api/_design //go:generate goagen -o gen app -d github.com/kyokomi/example_goa_api/_design //go:generate goagen -o gen swagger -d github.com/kyokomi/example_goa_api/_design
_design下を変更したときの作業手順は以下のような感じになります。
- _design下を編集する
- ルートディレクトリでgo generateする
- 生成された
gen/main.go
から追加したリソースのMountのコードをルートディレクトリのmain.go
にコピペする gen/
下に生成されたリソースのControllerをapplication/controllers
下にコピーしてimport等を直す(リソース追加ではなく編集の場合は、必要なMethodだけコピペする)
swaggerの配布について
werckerのworkflowの設定はこんな感じです。
build
のworkflowはexampleのリポジトリをご覧ください(https://github.com/kyokomi/example_goa_api/blob/master/wercker.yml)。 build
のworkflowが終わったあとに design/*
のブランチ名だったら put-swagger
のworkflowが実行される感じになってます。
put-swagger: box: golang:1.8 push-to-ecr: steps: - script: name: set timezone and deploy tag code: | export VERSION=`cat $WERCKER_ROOT/VERSION` echo $VERSION - edgecaseadmin/install-aws-cli: key: $AWS_ACCESS_KEY_ID secret: $AWS_SECRET_ACCESS_KEY region: $AWS_REGION // s3にswagger.jsonをコピーする - script: name: s3 put swagger code: | aws s3 cp $WERCKER_ROOT/gen/swagger/ s3://swager.example.dev/$WERCKER_GIT_REPOSITORY/$VERSION/ --recursive // s3に配置したswaggerのjsonをurlに指定したswagger-uiのリンクをpullRequestコメントする - script: name: swagger github comment code: | go get github.com/kyokomi/gh-rice gh-rice \ -t "$GITHUB_TOKEN" \ -o "$WERCKER_GIT_OWNER" \ -r "$WERCKER_GIT_REPOSITORY" \ -b "$WERCKER_GIT_BRANCH" \ -c "http://swagger.example.dev/?url=http://swagger.example.dev/$WERCKER_GIT_REPOSITORY/$VERSION/swagger.json"
※s3のパスとかは適当です
exampleコード
ヘルスチェックのAPIくらいしか書いてないのであれですが、参考になればと。
gormのCallbackをつかってexplain結果をログ出力する
はじめに
explainは基本的に実装時に自分で叩いてると思いますが、dev環境とかそこそこデータとか利用頻度が高い環境で雑に垂れ流したいなーと思ってgormのDebug実装を眺めていたらCallbackという仕組みがあったので、それを使ってみました。
作ったもの
使い方
gormのexampleをにちょこっと追加してみました。
package main import ( "fmt" _ "github.com/go-sql-driver/mysql" // mysqlを使う "github.com/jinzhu/gorm" explain "github.com/kyokomi/gorm-explain" ) type options struct { user string password string host string port int dbName string location string } func buildDataSourceName(opts options) string { const format = "%s:%s@tcp(%s:%d)/%s?parseTime=True&loc=%s" return fmt.Sprintf(format, opts.user, opts.password, opts.host, opts.port, opts.dbName, opts.location) } // Product product table struct type Product struct { gorm.Model Code string Price uint } func main() { opts := options{ user: "test-user", password: "test-user", host: "127.0.0.1", port: 3306, dbName: "test_db", location: "UTC", } db, err := gorm.Open("mysql", buildDataSourceName(opts)) if err != nil { panic("failed to connect database") } defer db.Close() db = db.Debug() // query trace db.Callback().Query().Register("explain", explain.Callback) // add explain callback // Migrate the schema db.AutoMigrate(&Product{}) // Create db.Create(&Product{Code: "L1212", Price: 1000}) // Read var product Product db.First(&product, 1) // find product with id 1 db.First(&product, "code = ?", "L1212") // find product with code l1212 // Update - update product's price to 2000 db.Model(&product).Update("Price", 2000) // Delete - delete product db.Delete(&product) }
実行した結果
(あと、gormのDebug機能をつかうとqueryがログに吐かれるようになるのでこれを合わせて使うと便利です)
(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:53) [2017-05-05 18:16:38] [1.08ms] INSERT INTO `products` (`created_at`,`updated_at`,`deleted_at`,`code`,`price`) VALUES ('2017-05-05 18:16:38','2017-05-05 18:16:38',NULL,'L1212','1000') (/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:57) [2017-05-05 18:16:38] [0.95ms] SELECT * FROM `products` WHERE `products`.`deleted_at` IS NULL AND ((`products`.`id` = '1')) ORDER BY `products`.`id` ASC LIMIT 1 +-------+----------------+----------+---------------+---------+------------------+--------+------------+--------+---------+-------------+--------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +=======+================+==========+===============+=========+==================+========+============+========+=========+=============+========================================================+ | 1 | SIMPLE | | | | | | | | | | Impossible WHERE noticed after reading const tables | +-------+----------------+----------+---------------+---------+------------------+--------+------------+--------+---------+-------------+--------------------------------------------------------+ 1 Fix example to add query trace (/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:58) [2017-05-05 18:16:38] [1.11ms] SELECT * FROM `products` WHERE `products`.`deleted_at` IS NULL AND ((code = 'L1212')) ORDER BY `products`.`id` ASC LIMIT 1 +-------+----------------+-------------+---------------+---------+----------------------------+----------------------------+------------+----------+---------+-------------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +=======+================+=============+===============+=========+============================+============================+============+==========+=========+=============+=======================================+ | 1 | SIMPLE | products | | ref | idx_products_deleted_at | idx_products_deleted_at | 5 | const | 2 | 100 | Using index condition; Using where | +-------+----------------+-------------+---------------+---------+----------------------------+----------------------------+------------+----------+---------+-------------+---------------------------------------+ (/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:61) [2017-05-05 18:16:38] [0.86ms] UPDATE `products` SET `price` = '2000', `updated_at` = '2017-05-05 18:16:38' WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = '7' (/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:64) [2017-05-05 18:16:38] [0.88ms] UPDATE `products` SET `deleted_at`='2017-05-05 18:16:38' WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = '7'
注意点
さっと書いたので、fmtで出力してます。ちゃんと自前のロガーとかで出力したい場合は、このコード参考に自分で書いてください 🙏🏻 (もしくはPullRequestまってます 😇 )
おわり
explainの結果に filesortが含まれていたら〜みたいなHandlerを設定できるような感じに改修とかすると便利そうだな〜とか思ってます。
esaのAlfred WorkflowをGoで作りました
作ったもの
とりあえずtokenとチーム名を登録して記事を検索できるだけです。
注意点
ちなみに、esaのAPIは利用制限があるので検索しすぎにご注意ください
https://docs.esa.io/posts/102#2-3-0
現時点では、ユーザ毎に15分間に75リクエストまで受け付けます。 とのこと
alfred-esa-workflow作ったんだけど、 API利用制限が15分間に75リクエストということで速攻で使えなくなった :innocent: pic.twitter.com/0BRWGTXaM9
— きょこみ@k02 (@k_yokomi) January 28, 2017
参考にしたもの
使ったライブラリ
filterで一覧表示するのがこんなに楽に実装できて感謝
esaのClientすでに作っている方がいてesaのAPI仕様はあまり見ないでもサクッと作れました! 感謝
最初、直接 Open in Finder
したフォルダ内にバイナリを放り込んで開発してたんですが、 普通に xxx.alfredworkflow
をダブルクリックしてInstallして動作確認したかったので、 alfred-workflow-packager
を使うようにしました。(まあ、色々replaceしてzipに固めるだけっぽいですが)
ただ、これはMacでAlfred3がインストールされている前提のコードがあるので、ちょっと書き直してCIで動くようにしようかなと思ってます。
GoでAlfredWorkflowを作る手順(雑メモ)
AlfredWorkflowつくるぞ〜と思ってから最初どうすればいいかわからない具合いが結構すごかったです。
とりあえず自分は、似てるworkflowをinstallして Alfred上で Open in Finder
して中身を漁ってつくりを把握してつくってみました。
思ったより単純な構造で、基本的には以下の流れでつくればいいのかなという理解。
- 普通にCLIツールをGoで作る
-
Script filter
での出力はgo-alfred
等を使ってxmlをPrintする Run Script
してPost Notification
に表示する出力は普通にfmt.Print
でOK
-
- Alfred上でkeywordsとかをとりあえず作成して
./<バイナリ名> search "{query}"
みたいな感じでCLIツールの呼び出しする Open URL
とか適当なActionを紐付けて、とりあえず動くところまでいくOpen in Finder
してicon.png
とinfo.plist
だけコピーしてGoのProjectにコピーする- あとは
alfred-workflow-packager
を使ってxxx.alfredworkflow
を作成して、ダブルクリックしてインストールして動作確認していく感じ
まとめ
Alfred便利なのでどんどん作っていくぞ!!!!!!
昔作ったgaego-initをglide対応した
gaego-initとは以下のように、GAE/Goの開発環境をコマンド一発でgenerateするやつです。
$ gaego-init -app hoge-app $ tree hoge-app/ hoge-app/ ├── Makefile ├── README.md ├── appengine │ ├── app.yaml │ └── init.go └── circle.yml 1 directory, 5 files
以前作ったときは、vendoringの対応をどうするかべきかわからず、結局shellでgoapp getするという荒業をしていたのでそれを修正しました。
# Makefile 22:26:56 APP_ROOT=appengine VENDOR_GOPATH=$(shell pwd)/$(APP_ROOT)/gopath/vendor APP_GOPATH=$(shell pwd)/$(APP_ROOT)/gopath RUN_GOPATH=$(APP_GOPATH):$(VENDOR_GOPATH) install: glide init glide install mkdir -p $(APP_ROOT)/gopath/vendor ln -s $(shell pwd)/vendor/ $(VENDOR_GOPATH)/src run: GOPATH=$(RUN_GOPATH) goapp serve $(APP_ROOT) clean-run: GOPATH=$(RUN_GOPATH) goapp serve --clear_datastore $(APP_ROOT) deploy: GOPATH=$(RUN_GOPATH) goapp deploy $(APP_ROOT)
こちらの記事を参考にglideでのvendoringを行えるようにしました。
感謝 🙏 🙏 🙏 🙏 🙏
Arukasを使って無料でGo製のslackbotを運用する
はじめに
SlackbotをHerokuで一日中動かすと課金が発生するので色々ハックが必要となったりするので、なんとかしたくArukasに出会いました。
今回は、自分が作ったgo製のslackbotを元につくったbotをgithubにpushしてWercker経由でdockerImageを作ってArukasへのDeployを行う方法を紹介します。
今回のコードとか設定をexampleとして公開してますので、よろしければ参考にしていただければと。
Arukasとは?
いまのところ無料でDockerをホスティングできるサービスです。
一応CLIツールも公開されていて、結構便利です。
※注意点としては、CMDで起動したプロセス監視とかは無いので自分でとかgo-server-starterとかを入れてプロセス死んだときに再起動する仕組みを入れる必要があるところです。
流れ
- 以前作ったgo製のslackbotのdockerImageをWerckerで作成する kyokomi.hatenablog.com kyokomi.hatenablog.com
- Arukasのコンソールでアプリケーションを作成し、dockerImageを指定する
- WerckerのworkflowでArukasのrestartを設定する
slackbotのコード自体はDockerとか意識しない形になっているので、wercker.ymlとかを見ていただければと。
example-go-slackbot/wercker.yml at master · kyokomi/example-go-slackbot · GitHub
Werckerの設定
README.md に書いてある通り6つの環境変数の登録が必要です。
- DockerHubまわりは、普通に自分のアカウントとかリポジトリを入れてもらえればOKです
- Arukasまわりは、 https://app.arukas.io/settings/api-keys で発行したAPIKeyとSecretを設定してください
ポイントはdocker-pushのworkflowで github.com/lestrrat/go-server-starter/cmd/start_server
をinstallしてます。
まあ別にバイナリをdonwloadでもいいんですが面倒だったので...
docker-push: box: golang steps: - glide-install - setup-go-workspace - script: name: install deamontools code: | go get github.com/lestrrat/go-server-starter/cmd/start_server - script: name: install application code: | go install - internal/docker-push: username: $DOCKER_HUB_USERNAME password: $DOCKER_HUB_PASSWORD tag: latest repository: $DOCKER_HUB_REPOSITORY registry: https://registry.hub.docker.com
arukas-deployのworkflowでは、 arukasのバイナリをdownloadして適当にPATHを通してstop -> startしてます。
sleep 10
を挟んでいるのは、stopする前にstartするとエラーになるので仕方なくです。
arukas-deploy: box: golang steps: - script: name: install tools code: | sudo apt-get update sudo apt-get -f install sudo apt-get install -y wget unzip curl tree - script: name: arukas install code: | mkdir -p $HOME/lib export PATH=$PATH:$HOME/lib cd $HOME/lib wget https://github.com/arukasio/cli/releases/download/v0.1.2/arukas_v0.1.2_linux_amd64.zip unzip arukas_v0.1.2_linux_amd64.zip rm arukas_v0.1.2_linux_amd64.zip - script: name: arukas restart code: | arukas stop ${ARUKAS_CONTAINER_ID} sleep 10 arukas start ${ARUKAS_CONTAINER_ID}
Arukasの設定
- SlackbotのTokenは、
Bots
かhubot
で作ったTokenを使ってください - docomoのAPIは雑談で使ってます(必須じゃないので不要なら設定しなくてOKです)
- REDISTOGO_URLは、
redis://<id>:<password>@<host>:<port>
の形式で指定します- 雑談のContext保持とCronコマンドの保存に使っています
- Redis To Go のfree planを使うのがおすすめです
- CMDでgo-server-stater経由でbotを起動するように指定してます
おまけ
ちゃんとプロセス死んで再起動するかの動作確認ですが、昔ネタでいれたcommandを実行させるプラグインが役に立ちました。(危険)
何度でも蘇る。