go-unitypackageっての作った

https://github.com/ToQoz/go-unitypackage

.unitypackageに初めて触れてみてて、クリックしてインストールしたのは良いものの、アンインストールできなくて辛すぎて作った。(Pro版は知らん)

あと、

  • ファイルリスト取って全部 .gitignore にぶっこみたい(色々ぶちまけられるので手で設定するのは無理)
  • 使用する .unitypackage のファイルリストにバッティングするものがないかどうか知りたい
  • 依存unitypackageを一括インストール、その後にゴニョゴニョ みたいなことをするスクリプトとか書きたい(「インストール後ここ書き変えて」ってpackageがあったから)

みたいな気持ちがあった。

まあ、とりあえずはおおまかにそのようなことが出来るようになったけど、一般的にこのへんの管理マジでどうやってるのか知りたい。 (unitypackageが吐き出すファイルをignoreせずにimportするごとにcommitしておいてアンインストールはrevertみたいなのはあんまりやりたくない...)

gog 紹介

http://github.com/ToQoz/gog

Logo of 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 で使いたかったというのもあるけど、

みたいな想いを持っていたことを思い出したことが大きい。

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 参考にすれば良いと思う。

c.f. https://github.com/heroku/heroku/blob/69cb44a2945abdc3eecef3c99ed3f91ef1e2d715/lib/heroku/command/apps.rb#L268

それとは関係なく、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.cssweathericons-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を作る
  • TQWeatherIconsFAKWeatherIcons.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とかはそもそもフィールドを指定しないけど、そういうことは起こらない。