GAE/Go 環境で echo が実行できない問題
問題
- GAE/Go 環境で echo を使ったアプリケーションを
goapp serve
で実行しようとすると以下のエラーが発生して実行できない
2017/01/16 16:08:42 go-app-builder: Failed parsing input: parser: bad import "unsafe" in github.com/valyala/fasttemplate/template.go from GOPATH
原因
- echo が依存している fasttemplate でエラーが出ている
- Issuesは出ている
- 修正パッチのPull requestも出ている
対策
Go 言語のアプリを heroku にデプロイするときは vender を .gitignore にいれるとだめ
- ビルドエラーになる
- godep 使わせている意味なくない?
- デプロイ用のブランチ作って .gitignore から外すとかするのかな?
$ git push heroku master Counting objects: 280, done. Delta compression using up to 4 threads. Compressing objects: 100% (260/260), done. Writing objects: 100% (280/280), 36.31 KiB | 0 bytes/s, done. Total 280 (delta 123), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Go app detected remote: -----> Checking Godeps/Godeps.json file. remote: -----> Installing go1.6... done remote: -----> Running: godep go install -tags heroku ./... remote: my_go_file.go:5:2: cannot find package "any/dep/package" in any of: ... 略 ... remote: godep: go exit status 1 remote: ! Push rejected, failed to compile Go app. remote: remote: ! Push failed remote: Verifying deploy... remote: remote: ! Push rejected to myapp. remote: To https://git.heroku.com/myapp.git ! [remote rejected] master -> master (pre-receive hook declined) error: failed to push some refs to 'https://git.heroku.com/myapp.git'
Rubyのリファクタリングでイケてないコードをなんとかするやつをやってみた
はじめに
- http://tango-ruby.hatenablog.com/entry/2016/07/15/133122 が面白かったので自分でもやってみた。
リファクタリングできそうな点
- initialize で start_date, end_date を渡しているのが不自然。
- OrdersReport という汎用的なクラス名なので期間以外の条件で絞り込みたくなった時に応用が効かない
- クラス名が OrdersDailyReport だったり、仕様として集計時には必ず期間が指定されると決まっていたりしたら気にしなくていいかも
- total_sales_within_date_range で orders の絞り込みと集計を同時にやっている
- ここも他の条件で絞り込みたくなった時に応用が効かないので絞り込み処理と集計処理を分けたい
リファクタリング結果
変更後
- 呼び出し方は変わって
OrdersReport.new(orders).select_within_date_range(start_date, end_date).total_amount
となった - 行数が削減された
- 複数の責務を持つ一つの大きなメソッドから単一の責務を持つ複数の小さなメソッドに分割された
- 絞り込み処理と集計処理をメソッドチェーンで繋げるようにしたので、新しい絞り込み条件が入ってきても拡張しやすくなった
require 'ostruct' class OrdersReport def initialize(orders) @orders = orders end def select_within_date_range(start_date, end_date) select { |o| start_date <= o.placed_at && o.placed_at <= end_date } end def select(&block) self.class.new(@orders.select(&block)) end def total_amount @orders.map(&:amount).inject(:+) end end class Order < OpenStruct end
変更前
require 'ostruct' class OrdersReport def initialize(orders, start_date, end_date) @orders = orders @start_date = start_date @end_date = end_date end def total_sales_within_date_range orders_within_range = [] @orders.each do |order| if order.placed_at >= @start_date && order.placed_at <= @end_date orders_within_range << order end end sum = 0 orders_within_range.each do |order| sum += order.amount end sum end end class Order < OpenStruct end
Go 言語でソリティアを作った
termbox-go を知ったので、息抜きに Go 言語でソリティアを作りました。
https://github.com/nirasan/go-solitaire
仕事中にやりやすいように ls っぽいモードも搭載してあります。
mac 用だけサンプルがあります。操作は README で。
https://github.com/nirasan/go-solitaire/blob/master/sample/solitaire?raw=true
Go 言語の列挙型的な定数と定数でのフラグ管理
列挙型的定数
- Go では const と iota を使って列挙型的な定数を定義する
- iota は const 宣言開始時に 0 になり、定数を定義する毎にインクリメントする
- 定数定義は値を指定しないと直前の定数と同じ値になるので iota と組み合わせることで連続した値が定義される
// 定義 type Flag int const ( // iota = 0 (仮想コード) Flag1 Flag = iota // Flag1 = iota; iota++ Flag2 // Flag2 = iota; iota++ Flag3 // Flag3 = iota; iota++ ) // 利用例 f := Flag1 if f == Flag1 { // Flag1 の場合の処理 }
列挙型的定数を使ったフラグ管理
- 列挙型的な定数の定義時に一工夫すると定数をフラグ的に管理できるようになる
// 定義 type Flag int const ( Flag1 Flag = 1 << iota // 1 << 0 (10進数: 1, 2進数: 00000001) Flag2 // 1 << 1 (10進数: 2, 2進数: 00000010) Flag3 // 1 << 2 (10進数: 4, 2進数: 00000100) Flag4 // 1 << 3 (10進数: 8, 2進数: 00001000) ) // 利用例 f := Flag1 if f & Flag1 != 0 { // Flag1 の場合の処理 // &(and) 演算子で双方にビットが立っていた場合に 0 以外が返るので Flag1 が有効かどうかの判定ができる } if f & (Flag1 | Flag2) != 0 { // Flag1 か Flag2 の場合の処理 // |(or) 演算子で Flag1 かつ Flag2 の値を作ることができるので複数のフラグの判定処理がちょっと楽になる }
ひとつの定数で複数種類のフラグを管理する
- 例えば文字の色と書体を同一の方で管理するような場合
type Flag int const Flag_N_Start = 0 const ( Flag1 Flag = 1 << (iota + Flag_N_Start) // 1 (00000001) Flag2 // 2 (00000010) Flag3 // 4 (00000100) Flag_N_Bits = iota // 3 (Flag_N で 3bit 使っている) Flag_N_Mask = 1 << Flag_N_Bits - 1 // 7 (00000111) (Flag_N 用のビットマスク) ) const Flag_A_Start = Flag_N_Bits // Flag_A の開始ビット const ( FlagA Flag = 1 << (iota + Flag_A_Start) // 1 << (0 + 3) = 8 (00001000) FlagB // 1 << (1 + 3) = 16 (00010000) FlagC // 1 << (2 + 3) = 32 (00100000) Flag_A_Bits = iota // 3 (Flag_A で 3bit 使っている) Flag_A_Mask = (1 << Flag_A_Bits - 1) << Flag_A_Start // 56 (00111000) (Flag_A 用のビットマスク) (使用ビット分だけマスクを作って開始ビット分だけ左シフトする) ) f := (Flag1 | FlagB) if f & (Flag1 | FlagA) != 0 { // Flag1 か Flag2 だった時の処理 }
termbox-go 使い方まとめ
はじめに
https://github.com/kurehajime/pong-command とかみたいな感じでターミナル上で任意の座標に任意の文字を表示できる termbox-go の使い方まとめ。
概要としては termbox の内部でターミナル上の座標に対応した文字のバッファを持っているので、SetCell で座標と文字などを指定して、Flush で表示するという感じ。
使い方
インストール
go get github.com/mattn/go-runewidth // termbox-go が参照している go get github.com/nsf/termbox-go
初期化
err := termbox.Init() if err != nil { panic(err) } defer termbox.Close()
バッファのクリア
文字色と背景色を指定してバッファをクリアする
色の一覧は https://godoc.org/github.com/nsf/termbox-go#Attribute
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
文字のセット
https://godoc.org/github.com/nsf/termbox-go#SetCell
termbox.SetCell(1, 2, rune("a"), termbox.ColorDefault, termbox.ColorDefault)
文字の取得
termbox 内部のバッファから文字を取得する。
表示済みの文字の色だけ変える場合などに使用できる。
以下は2行目の3文字目を取得する。
width, _ := termbox.Size() row := 1 col := 2 cell := termbox.CellBuffer()[(width*row)+col] var char rune = cell.Ch var fg termbox.Attribute = cell.Fg var bg termbox.Attribute = cell.Bg
バッファの表示
termbox.Flush()
キーボード入力の受付
mainloop: for { switch ev := termbox.PollEvent(); ev.Type { case termbox.EventKey: if ev.Key == termbox.KeyEsc { break mainloop // Esc で実行終了 } case termbox.EventResize: // 画面のリサイズが発生したので再描画などをする } }
マウス入力の受付
mac の Terminal.app や iTerm2.app では現状バグが有って正常に動かない様子(マウス入力時に Esc など他のキー押下イベントが発生してしまう)
https://github.com/nsf/termbox-go/issues/120
termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse) mainloop: for { switch ev := termbox.PollEvent(); ev.Type { case termbox.EventMouse: if ev.Key == termbox.MouseLeft { mx := ev.MouseX my := ev.MouseY } }
キーボードのアルファベットキーを取得する
- termbox のデフォルトのキーイベントではアルファベットキーをそのままは受け取れないので
github.com/jteeuwen/keyboard/termbox
を使う
import keyboard "github.com/jteeuwen/keyboard/termbox" func main() { // termbox の初期化などは省略 pollEvent() } func pollEvent() { running := true kb := keyboard.New() kb.Bind(func() { running = false }, "escape", "q") kb.Bind(func() { /* do up */ }, "up", "k") kb.Bind(func() { /* do down */ }, "down", "j") kb.Bind(func() { /* do left */ }, "left", "h") kb.Bind(func() { /* do right */ }, "right", "l") for running { kb.Poll(termbox.PollEvent()) } }
文字色を数字で指定する
- 文字色や背景色は termbox.Attribute 型にキャストすれば数字で直接指定できる
// 256色表示(デフォルトは8色) termbox.SetOutputMode(termbox.Output256) // 文字色を背景色を数字で指定(内部で-1されているので+1する) termbox.SetCell(0, 0, "a", termbox.Attribute(100+1), termbox.Attribute(200+1))
参考アプリケーション
- termbox-go を使って実装されているアプリケーションを参考のためにいくつか
Go 言語でブログを作ったメモ
- Go 言語学習のためにユーザー認証とDB連携のあるブログアプリを作った
- ソースは https://github.com/nirasan/go-blog
- Web 周りに Gorilla web toolkit を、ORM に gorp を使用
- Go 自体シンプルだし、あえてシンプルなライブラリを選んでみたのでわりとはまらずにできた
- 次は revel とかリッチめのライブラリ試すかこのままシンプルで行くかどうするか