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とかはそもそもフィールドを指定しないけど、そういうことは起こらない。

https://github.com/user.keys をとってくるgemつくった

似たようなのありそうだけど、探すより作ったほうが早そうだったからつくった。

chefとかでpub key登録する時により楽にしたかった。 コマンドラインからも使えるので、手動で登録するような時もちょっとだけ役に立つと思う。

$GOROOT/src/race.bashとかのインデント

もハードタブだったの、どっちでも良いんだけど面白かった。Cとかも。

変な意味ではない。単純にgoでの標準はそうだから、goでのみハードタブにするし、go fmtで保証されるからどっち派とか関係なくハードタブで良いんだよ。みたいな感じだと思ってたけど、(少なくともgolangリポジトリでは)goに限らずインデントにハードタブ使ってんだなーと。

まあでも普段ハードタブ使ってない人が言語作ってハードタブを標準のフォーマットにしようとか思わなさそうだし、普通と言えば普通だと思った。

plistのシンタックスチェック

plistをロードできなくて、なにかしらダメなんだろうけど、launchctl: no plist was returned for: /Users/toqoz/Library/LaunchAgents/<PLIST_FILE> とだけ言ってくる。

とりあえずシンタックスエラーでもチェックしようと思って調べたら、plutil -lint ~/Library/LaunchAgents/<PLIST_FILE> というので出来た。

c.f. http://apple.stackexchange.com/a/46380

homebrewで入れたgoの$GOROOT

$BREW_HOME/Cellar/go/1.1.1 でよかったんだけど、https://github.com/mxcl/homebrew/commit/743bf42e2e0a5f0fc217b114e88c24f84c1fed97 以降は $BREW_HOME/Cellar/go/1.1.2/libexec としてやらないといけなくなった。

なんだけど、このtweetをたまたま見て、そもそもGOROOTはビルド時と違う場所に置いたりしてない限り別に設定しなくて良いらしい。ということを知った。

で少し調べた。

$ echo $GOROOT

$ go env
GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH=""
GORACE=""
GOROOT="/Users/toqoz/brew/Cellar/go/1.1.2/libexec"
GOTOOLDIR="/Users/toqoz/brew/Cellar/go/1.1.2/libexec/pkg/tool/darwin_amd64"
CC="gcc"
GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread -fno-common"
CGO_ENABLED="1"

pkg/runtime/zversion.godefaultGoroot って定数で持ってた。build時に http://golang.org/src/cmd/dist/buildruntime.c#L18 でそれを作ってる。

cf. http://golang.org/doc/install#tarball_non_standard

iOS remindersとかcalendarみたいな日付選択

iOSやるやる言いつつ、本読んでちょっと試して放置して2年ぐらい経つ気がするけど、先月ちょっと作ろうと思うものがあって始めた。

remindersとかcalendarみたいな日付選択を作りたくなって試してみた。

  • _datePickerCell, _dateFieldCellってクラス作ってnilと紐付ける。
  • _dateFieldCellを押した時に、_datePickerCell_cellsになかったら追加し、あれば削除
  • _datePickerCell編集開始時に_dateFieldCellの色を編集中の色にする、終れば確定の色にする。
  • _datePickerCellの値が変った時に_dateFieldCellの値を変える。

みたいなことをやった。(実際のコードから流れを簡易化するためにsectionを1個にしたり適当に手を加えた)

- (void)viewDidLoad
{
    // initialize _dateFieldCell, _datePickerCell
    // ...
    _cells = @{
        // ...
        _dateFieldCell
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([self cellForIndexPath:indexPath] == _datePickerCell) {
        return _datePickerCell.picker.frame.size.height;
    }

    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [_cells count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [_cells indexPath.row];
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *selectedCell = [self cellForIndexPath:indexPath];

    if (selectedCell == _dateFieldCell) {
        [self toggleDatePickerCell];
    } else {
        // ...
    }

    [self closeSoftKeyboard];
}

- (void)toggleDatePickerCell
{
    if ([_cells containsObject:_datePickerCell]) {
        [self _hideDatePickerCell];
    } else {
        [self _showDatePickerCell];
    }
}

- (void)_showDatePickerCell
{
    int targetIndex = [NSIndexPath indexPathForRow:[_cells indexOfObject:_dateFieldCell] + 1;

    [self.tableView beginUpdates];
    [_cells insertObject:_datePickerCell atIndex:targetIndex];
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:targetIndex inSection:0]]
                          withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView endUpdates];

    // 日付を編集中の色にしたり
    [_dateFieldCell didStartEditing];
}

- (void)_hideDatePickerCell
{
    int targetIndex = [NSIndexPath indexPathForRow:[_cells indexOfObject:_dateFieldCell] + 1;

    [self.tableView beginUpdates];
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:targetIndex inSection:0]]
                          withRowAnimation:UITableViewRowAnimationFade];
    [_cells removeObjectAtIndex:targetIndex];
    [self.tableView endUpdates];

    // 日付を確定の色にしたり
    [_dateFieldCell didEndEditing];
}

// DatePickerCell delegate(DatePickerCellが変更された時)
- (void)dateDidChange:(UIDatePicker *)datePicker
{
    _dateFieldCell.date = datePicker.date;
}

- (void)closeSoftKeyboard
{
    [self.tableView endEditing:YES];
}

bundle install --path=PATH で入れたgemを認識してほしい、Intellij

.bundle/config を見てくれないっぽくて困ってて、File -> Project Structure -> Modules にそのプロジェクトのLOAD_PATH設定する所を発見したけど

...
 "/Users/toqoz/dev/[a project]/vendor/gems/ruby/2.0.0/gems/uglifier-x.x.x/lib",
 "/Users/toqoz/dev/[a project]/vendor/gems/ruby/2.0.0/gems/turnip-x.x.x/lib",
 "/Users/toqoz/dev/[a project]/vendor/gems/ruby/2.0.0/gems/susy-x.x.x/lib",
....

みたいなのを一々追加できる人はいないのでどうしたものかと思っていたら、File -> Project Structure -> SDKs -> [a ruby version]Classpathってのがあって、

/Users/toqoz/dev/[a project]/vendor/gems/ruby/2.0.0/gems

を追加してやれば良かった。これだとプロジェクトごとにSDK作る必要ありそうだけど、とりあえずはこれでいいかなという感じ。

goで特定のエラーに対して別々にハンドリングする

  • エラーメッセージの比較
  • errorの比較
  • errorの型の比較

errorの比較がシンプルで好きだけど、それだけだと、動的にエラーメッセージ変えたい時とかに困る。

Demo: http://play.golang.org/p/F9qAcLoMhP