go-unitypackageっての作った
https://github.com/ToQoz/go-unitypackage
- unity == http://unity3d.com
.unitypackage
に含まれてるファイルリスト取ったり、コマンドラインからインストール/アンインストールできるようにする
.unitypackage
に初めて触れてみてて、クリックしてインストールしたのは良いものの、アンインストールできなくて辛すぎて作った。(Pro版は知らん)
あと、
- ファイルリスト取って全部
.gitignore
にぶっこみたい(色々ぶちまけられるので手で設定するのは無理) - 使用する .unitypackage のファイルリストにバッティングするものがないかどうか知りたい
- 依存unitypackageを一括インストール、その後にゴニョゴニョ みたいなことをするスクリプトとか書きたい(「インストール後ここ書き変えて」ってpackageがあったから)
みたいな気持ちがあった。
まあ、とりあえずはおおまかにそのようなことが出来るようになったけど、一般的にこのへんの管理マジでどうやってるのか知りたい。 (unitypackageが吐き出すファイルをignoreせずにimportするごとにcommitしておいてアンインストールはrevertみたいなのはあんまりやりたくない...)
gog 紹介
静的サイト作るやつみたいな感じ。一般的なものにくらべて、機能が著しく少ない。_layout.html書いといてヘッダーフッター共通化しか機能がない。
ただ各ディレクトリに、GOG_BUILD.go
ってやつ置けて、それはgo run
される。置いておくと、例えば、build時にどっかからjson取ってきてHTML生成するみたいなことができる。
別のAPI経由でDB更新していって、jenkinsとかに3分に一回HTMLをbuildさせててたりすると、動的サイトみたいになれる。ユーザーからリクエストが来た時に計算はしなくても、動けるのかということを考えたりした。やらないけど。
それはそうと、なんでこういう機能を付けたかというと、http://github.com/ToQoz/toqoz.github.io/tree/master/_src/go/pkg で使いたかったというのもあるけど、
今のテンション的にコンドル飛んでないし、テンションによってブログのタイトル変わって欲しい
— ピヨちゃんです (@ToQoz) December 25, 2012
みたいな想いを持っていたことを思い出したことが大きい。
golangでパッケージ作る時の名前をこうやって付けてます。という話
これが最高とかって話ではない。
gemとかにおけるキラキラネームの良さは、被らないことだと思う。しかし、goにおいてはimport pathが別の人が作ったのと被るのを気にしなくて良いから、良さは特にないと思っているけど単に好みで割と付けようとしてしまう。
大体 1 -> 2 -> 3 って感じで考えてるけど、その順番も毎回必ずそうかというとそうでもない。
1. しっくりくるキラキラネーム、ダジャレを思いついた時
それを使う。子供に奇抜な名前付けると、名前だけで識別できる。
- package: キラキラネーム
- repository: 同上
2. 1に失敗して、golangに関係した(golangで書いたではない)パッケージを作る時
つまりpackage名にgoを含めたいようなものを作る時(e.g. godoc)
- package:
go
prefix を付けて、ひねりのない名前を付ける。 - repository: 同上
3. それ以外(これはgolangで書いたやつですみたいなの)
go-
prefix を付けて、ひねりのない名前を付ける。packageには付けない。あくまでリポジトリの識別性を上げるためにgo-
って付けてる。(e.g. go-formspec)。近所に同じ名前の子がいたら名字一緒に呼んであげると良いと思う。
- package:
go-
prefix を付けずに、ひねりのない名前を付ける。 - repository:
go-
prefix を付けて、ひねりのない名前を付ける。
ただ2分前ぐらいに思ったのは、https://github.com/google/go-github みたいに、リポジトリに go-
prefix付けて、そこにprefixなしgithub
のディレクトリ掘ってそこに package github
とか置くやつのほうが良いかなと思った。
package名とimport pathの最後が一致してるのはやっぱり良いなーと。
testing.afterを自由にしたかった。テストの最後になんかしたかった。golang
追記
1.4から普通にできるようになった。
なにかと最後に掃除したりしたい例があった。
要は、application側が、
package main import ( "xxx.com/xxx/xxx/models" ) func main() { db, err := sql.Open() if err != nil { panic(err) } defer db.Close models.InitDbmap(db) // main }
やらなんやら、別にこの例はどうでも良いけど、全体の最後になにかするのが出来るんだから、testing.Mainも自由にやりたい、もしくはtesting.after書きかえたい、なにかを追加したいみたいな希望があった。
go test
は Package.TestGoFiles を順にロードして、testing.T.Testsにappendしてってるので、その最後に来るようにすれば良いかなーでもなーとか思ってる時に http://golang.org/src/pkg/net/http/z_last_test.go を発見したので、とりあえずこういう感じてやってこうかと思ってやってみている。
z_last_test.go
package models import ( "os/exec" "strings" "testing" ) func TestThisFileIsLastTestFile(t *testing.T) { cmd := exec.Command("go", "list", "-f", "{{.TestGoFiles}}") output, err := cmd.CombinedOutput() if err != nil { panic(err) } tests := strings.Split(strings.Trim(string(output), " \n[]"), " ") lastTest := tests[len(tests)-1] if lastTest != "z_last_test.go" { t.Errorf("expected last_test is z_last_test.go, but got %v", lastTest) } } func TestHogeHoge(t *testing.T) { // なんか }
golang初め
とうことで https://github.com/ToQoz/api というのを作った。
json api書く用に、ちょっと便利にしてくれるやつ。需要があるかはわからないけど、そういうの書いてる時に毎回こういう層準備するのが面倒で書いた。sinatraっぽいRouterのinterfaceに依存してるけど、特定のrouterに依存してなくて、そういうのが作りたかった。
そういうapi便利pkgのためにrouterに依存しちゃうと、あれもこれもみたいな感じになって、個人的なgolang感と離れてしまうのでそういう風にした。golangで書く時だと特にframeworkとかじゃなくて、薄いpkg組み合わせて書きたくなる。
- sinatraっぽいRouterのinterface
type Router interface { Get(string, http.Handler) Head(string, http.Handler) Post(string, http.Handler) Put(string, http.Handler) Delete(string, http.Handler) http.Handler }
keyremap4macbookで特定のアプリに対してだけキーマップを設定したい時は、組込みで入ってるxmlを参考にすると良い
特定のアプリに対してキーマップを設定するとか独自で定義するとかって時は, private.xmlに書けば良いんだけど、
例えば、macだと基本C-eで行末にいけるけど、xってアプリがC-eを上書きしてるから、それ使ってる時だけkeyremap4macbook側でC-eを行末移動に変えたいとかって時に、簡単に書けることもあれば、どう書けば良いかよくわからなかったりすることもある。
上記のようにキーバインド自体は組込みで入ってるのと一緒だったり、似ていたりする場合はそれを見るのが良いと思う。
/Applications/KeyRemap4MacBook.app/Contents/Resources/include/checkbox/emacs_mode.xml
さっきの例だとこれを見れば良い。
考えてみれば当然だけど、無意識にgoogle.comにアクセスした後我に帰って気付いた。
length: {maximum: 10} みたいなvalidatorを作る(is_a ActiveModel::EachValidator)
# Example # validates :email, email: true class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) fuzzy_valid_email_regexp = // record.errors.add(attribute, :invalid_email) unless fuzzy_valid_email_regexp =~ value end end
みたいなのは作ったことあったけど、任意の値を渡したくなるvalidatorを書くことがなかったので知らなかった。
UTF-8の文字列を扱ってるシステムなんだけど、深淵な理由からSJISにエンコードした時のbytesizeでvalidationしないといけないみたいな要件があり、調べた。特に特別にやらないといけないことはなかった。
# Example # validates :email, sjis_bytesize: {maximum: 100} class SjisBytesizeValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) sjis_bytesize = ほげほげ value if sjis_bytesize > options[:maximum] record.errors.add(attribute, :too_long, count: [:maximum]) end end end
みたいに options
を参照すれば良い。渡した値を事前に加工したりチェックしたければ initialize
でごにょごにょした後にsuper呼んどけば良い。
c.f.
heroku create -bをhkで
せっかくgolang webappなんだからheroku commandじゃなくhkでデプロイしたい。
と思ったけど、hk create -b
なくて、herokuあんまり触ったことなく知らなくて、一瞬どうせheroku commandいるんだ...的なテンションになってたけど、
$ hk create $ hk set BUILDPACK_URL=https://github.com/kr/heroku-buildpack-go.git
すれば良い。
他は大体 http://mmcgrana.github.io/2012/09/getting-started-with-go-on-heroku.html 参考にすれば良いと思う。
それとは関係なく、v20131205ぐらいのバージョン使ってたら、hk create後のherokuからのjsonがunmarshalできないのかエラー出るのでupdateすると良い。
$GOROOT/miscにはvim/emacs pluginだけじゃなく、便利なものが色々ある
例えば
$ cat $(go env GOROOT)/misc/git/pre-commit #!/bin/sh # Copyright 2012 The Go Authors. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # git gofmt pre-commit hook # # To use, store as .git/hooks/pre-commit inside your repository and make sure # it has execute permissions. # # This script does not handle file names that contain spaces. gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$') [ -z "$gofiles" ] && exit 0 unformatted=$(gofmt -l $gofiles) [ -z "$unformatted" ] && exit 0 # Some files are not gofmt'd. Print message and fail. echo >&2 "Go files must be formatted with gofmt. Please run:" for fn in $unformatted; do echo >&2 " gofmt -w $PWD/$fn" done exit 1
これを知らずに適当に自分で作ったりしてたけど、こっちを使うようにした。
misc/chrome/gophertool
とかもちょっと便利で、これにshortcut登録しておくとpkgのドキュメントに行ったりが楽になる。misc/goplay
はめちゃくちゃ便利で、gopalygroundじゃなく、ローカルだ試したいのって時に良い。powで、goplay.dev
でアクセスできるようにしてる。
どっかのドキュメント見てvim pluginはruntime path通しただけで、他になにがあるか見てなかったけど、ふと思いたって見てみると得した。
FontAwesomeKitはそれ自体に組み込まれてないアイコンフォントを使う場合にも役立つ
例えば、 https://github.com/erikflowers/weather-icons を使いたいって時の例。
weather-icons.css
とweathericons-regular-webfont.ttf
とかをリポジトリに入れておく。- コードジェネレータを準備(
CodeGenerator.rb
,gen.rb
) FAKWeatherIcons.fakgen.[h|m]
を生成(ruby gen.rb
)
$ tree WeatherIcons WeatherIcons ├── FAKWeatherIcons.fakgen.h ├── FAKWeatherIcons.fakgen.m ├── code_generator.rb ├── gen.rb ├── weather-icons.css └── weathericons-regular-webfont.ttf
weathericons-regular-webfont.ttf
をリソースに追加FAKIcon
を継承したTQWeatherIcons
を作るTQWeatherIcons
にFAKWeatherIcons.fakgen.[h|m]
をコピペして、singleton生成のためのクラスメソッド書く。
※ コードジェネレータを準備のとこで、提供されてるCSSに合わせたgen.rb
を適当に書く
require './CodeGenerator.rb' # https://github.com/PrideChung/FontAwesomeKit/blob/master/CodeGenerators/CodeGenerator.rb names = [] codes = [] _lines = File.read("./weather-icons.css").lines lines = [] _lines.each_with_index do |l, index| case l when /^\.wi-.+{/ lines << l + _lines[index + 1] + _lines[index + 2] end end lines.each do |line| name = '' line.gsub(/(?<=wi-).*(?=:before)/i) { |match| name = match } nameParts = name.split('-') nameParts = nameParts.each_with_index.map do |p, i| if i < 1 p else p = p.capitalize end end name = nameParts.join names.push name code = '' line.gsub(/".*"/) { |match| code = match[2..(match.length-2)] } codes.push "\\u#{code}" end generator = CodeGenerator.new('WeatherIcons', names, codes) generator.generate
こういう風に元々FontAwesomeKitに入ってないフォントでも、UIImage化したりとかFAKIconが提供している便利機能を使えて良い。
main packageが複数ファイルで構成される時のgo run
main.go
package main func main() { some() }
some.go
package main import ( "fmt" ) func some() { fmt.Println("some") }
というのがある時に、go run main.go
ってやると動きそうな気がするんだけど、動かない。
Run compiles and runs the main package comprising the named Go source files. A Go source file is defined to be a file ending in a literal ".go" suffix.
とあるように、go run main.go
とやっただけでは、main.goだけがコンパイルされる。同一packageゆえにmain.goからのimportなど明示的な依存関係があるわけではないので、some.goがコンパイルされることはない。
go run main.go some.go
なり go run *.go
なりしてやる必要がある。
iosのフォームバリデーションみたいなの
tableViewで作ってたので、その例で。
tableViewControllerをtextfieldのdelegateにしちゃうと、「これは何文字」とか、「これは必須」とかどうこうとか面倒なことになるなーと思って、そういう形にはしなかった。
一個のcellに一個のフィールドがあることが多いし、UITableViewCellをサブクラス化して、バリデーションすることにした。バリデーションメッセージを出すのもCellだけでやりたかったけど、cellからはみ出る吹き出しみたいにしたかったので、それはtableViewControllerにさせてる。
@protocol TQValidatableFieldDelegate - (void)cell:(UITableViewCell *)cell didResolveValidationErrorWithErrorView:(UIView *)errorView; - (void)cell:(UITableViewCell *)cell didOccurValidationErrorWithErrorView:(UIView *)errorView; @end // ---------- Custom Cell ---------- - (BOOL)validate { if ([self TQ_validate]) { [self.delegate field:self didResolveValidationErrorWithErrorView:_validationErrorView]; } else { [self.delegate field:self didOccurValidationErrorWithErrorView:_validationErrorView]; } return isValid; } - (BOOL)TQ_validate { if (_inputField.text.length == 0) { _validationErrorView.message = @"入力してください。"; return NO; } if (_inputField.text.length <= _minLength) { _validationErrorView.message = [NSString stringWithFormat:@"%d文字以上で入力してください。", _minLength + 1]; return NO; } if (_inputField.text.length > _maxLength) { _validationErrorView.message = [NSString stringWithFormat:@"%d文字以内で入力してください。", _maxLength]; return NO; } _validationErrorView.message = @""; return return YES; } // ---------- TableViewController ---------- - (void)cell:(UITableViewCell *)cell didReceiveValidationErrorWithErrorView:(UIView *)errorView { if (![errorView isDescendantOfView:self.tableView]) { [self.tableView addSubview:errorView]; [self.tableView bringSubviewToFront:errorView]; } } - (void)cell:(UITableViewCell *)cell didRemoveValidationErrorWithErrorView:(UIView *)errorView { if ([errorView isDescendantOfView:self.tableView]) { [errorView removeFromSuperview]; } }
textFieldのdelegateになってるcellが変更があったりした適当なタイミングでvalidateするのと、フォームをsubmitする時のtableViewControllerが全部のcellに対してvalidateするみたいな感じにしてる。その辺は省略。
best practiceとか大体こうやってるよみたいなのは知らない。
rsenseがドックに表示される時
いつのころからか、起こるようになったので、アドホックな対応を。
- そもそもは
-Dapple.awt.UIElement=true
を設定しないといけないアプリケーションではないと思う(しなくてもDockに出ないはずだと思う) - javaのバージョンが関係してた気がするけど、その辺詳しくない
ので、pull reqとかはせずこんな感じで。
--- /Users/toqoz/brew/Cellar/rsense/0.3/libexec/bin/rsense.orig 2010-05-11 11:52:52.000000000 +0900 +++ /Users/toqoz/brew/Cellar/rsense/0.3/libexec/bin/rsense 2013-12-12 12:07:54.000000000 +0900 @@ -103,7 +103,7 @@ def server_process(cont_proc = nil) begin - command = %W(java -cp #@classpath org.cx4a.rsense.Main script) + command = %W(java -cp -Dapple.awt.UIElement=true #@classpath org.cx4a.rsense.Main script) command += %W(--home=#@rsense_home --no-prompt --end-mark=#{END_MARK} --config=#@config) command << '--debug' if @debug command << "--log=#@log" if @log
$GOPATH内のリポジトリを楽に移動する
必要なもの
こんな感じ
$ cat ~/.zshrc | grep anything-gorepo alias gogo='cd $(anything-gorepo)'
#!/usr/bin/env ruby # anything-gorepo unless ENV['GOPATH'] STDERR.puts '$GOPATH should not be empty' exit 1 end require 'ruby-anything' def directories path Dir.entries(path).select do |filename| File.directory?(File.join(path, filename)) end.select do |filename| filename != '.' && filename != '..' end end gosrc = File.expand_path("#{ENV['GOPATH'].dup}/src") repo_paths = [] directories(gosrc).each do |host| directories(File.join(gosrc, host)).each do |user| directories(File.join(gosrc, host, user)).each do |repo| repo_paths << File.join(host, user, repo) # diplay without gopath end end end STDOUT.puts File.join(gosrc, _anything_(repo_paths))
anything-gorepoは適当な名前でgem化するかも知れない。
追記
anything-gorepoはgemにした。
gorpでSQL書いてレコード取得する時には必ず個別にカラムを指定したほうが良い話
SQLとして何が好ましいかという話はしないし関係ない。
SQL書いてSELECTするような場合、gorp.DbMap.Select
とか gorp.DbMap.SelectOne
とか。gorp.rawselect
ってのが呼ばれて、
そこでは、取得したカラムの全てがstructに無い場合エラーを吐くようになってる。
https://github.com/coopernurse/gorp/blob/87135bba705725c26b805636fd548fae440a1c6b/gorp.go#L1495
なので、例えばだけど、
dbmap.Select(&User{}, "SELECT * from users")
とかってしている- User struct にname追加, users tableにname追加するmigration作成
- migration実行
- app再起動
とかって場合、app再起動が終わるまでその部分はエラーを吐く。
gorp.DbMap.Get
とかはそもそもフィールドを指定しないけど、そういうことは起こらない。