2008年 07月 08日

ゆの in Ruby

#!ruby -Ku

class X
	attr_reader :a, :b

	def self./(a)
		new(a)
	end

	def self.to_s
		"×365"
	end

	def initialize(a)
		@a = a
	end

	def /(b)
		@b = b
		print self
		self
	end

	def <(o)
		puts " #{o}"
	end

	def to_s
		@a.a + "スケッチ" + b.to_s
	end

	class ::Object
		def _
			X.new("ひだまり")
		end
	end
end



X / _ / X < :来週も見てくださいね!

正式名称になるように一文字足しました

ゆの in Io-language

X := Object clone do (
	/ := method(
		if (self ?called, "スケッチ", "ひだまり") print
		self called := true
		self
	)

	< := method(o,
		"365" print
		o println
	)
)


X / _ / X < "来週も見てくださいね!"

このコードにおいて、 _ は評価されない ( / の引数なのだけど、/ 内では _ を評価していない) ので定義してなくてもいいところがポイントです。?called は、初回呼び出し時の called スロットがないために起こるエラーを回避します (正確な?の効果は「対象オブジェクトが指定メッセージに応答しないならば、メッセージセンドを行わない」かな? つまり ? でわっしょい!)。

文字列リテラルを使わずに call argAt(0) とかもできるはずなんですが、どうもマルチバイトだとダメみたいで諦めてます。

2008年 07月 06日

Wassr 用の IRC ゲートウェイ

http://coderepos.org/share/browser/lang/ruby/net-irc/trunk/examples/wig.rb

いままで tig.rb を継承して wig.rb を作っていたのですが、割と API に互換性がないのと、「チャンネル」とかに対応させるたりしたいために wig.rb は tig.rb をコピペしながら書きなおしました (ヘタに混在させてカオスにするよりはコピペのほうが健全)

tig.rb からの変更点

  • グループ機能をさくじょ (/join #foo とかは、ワッサーのチャンネルをヲチるようにするため)
  • ワサチャンネルへの対応 (jabber つかってても api 使っててもいけるはず。ただ api のほうは制限つけたりをちゃんとやってないのでそのうちだめなになるかも。今は wassr は api 制限ないので問題ない)
  • いろいろリファクタリング (tig.rb にも書き戻す予定)

ちゃんと説明すると (というか忘れそうなので現状の整理)

  • チャンネル名は、ワサチャンネルの英語名 (id) です。
  • jabber を使わない場合
    • 明示的に join したワサチャンネルだけが通知される ( /join #wassr_request ) とか
    • 現状ではワサチャンネルに参加する API がないので、先に web から参加しておく必要がある
    • 参加していないワサチャンネルでも、更新情報だけは /join するだけで見れる (単純に json feed みてるだけなので)
    • 入れば入るほど API アクセスが増えます
  • jabber を使う場合
    • 明示的にチャンネルに join しなくても、参加しているワサチャンネルのメッセージ自体は送られてくる (サーバメッセージの欄に表示されたりするはず)
    • いくらワサチャンネルに参加してても jabber コネクションは常に一つです
    • jabber 使うのが現状では一番効率的です。

IRC の「チャンネル」とワッサーの「チャンネル」が混ざるので、ワッサーのチャンネルは「ワサチャンネル」と書きなおしました

IM からでも /me fav id が使えるようにした。

  • ユーザごとにカウンタをもつ
  • 発言するたびにカウンタをインクリメント
  • id -> [nick, counter] のハッシュをつくっておく
  • /me fav id をしたときに nick の人の user_timeline をとってきて、counter の示す位置の status の id を取得
  • イイネの API を叩く


問題点

  • user_timeline がキャッシュされてる?っぽいので、ズレることがある。
  • タイミングによってはズレる。
  • とりあえずズレる

Terminal.app にもどった

iTerm 重すぎてだめだ…… 軽くなるパッチがつくれたらもどろう。。。

2008年 07月 05日

make -j 2

make するとき常に gcc とかが一個しかうごいてなくて、マルチコア CPU だと 50% までしか使わなくて残念なかんじだなーっておもったら、make するとき -j 2 とか渡すと依存関係中で平行できるところは平行してやってくれるみたいだった。make -j だけだと起動しまくって重すぎなので、コアの数を指定するとよさそう?

2008年 07月 04日

git-svn dcommit を自動的に行う

svk で mirror レポジトリに直接コミットするような感じにしたいときがいくらかありますよね。普通に git ci / git svn dcommit をやるんでもいいんですが、これだと git の軽さが損なわれてしまい、とても残念な気持ちになります。なので、git svn dcommit は非同期で裏に回すようにします。


しかし、けっこうこういう、裏にまわして非同期で実行して結果だけみたい場合っていうのはあるので、汎用化しておきます。結果表示はうまくいったかどうかだけわかればいいので、screen のラインに表示させることにしました。

backtick は wait を 0 に設定すれば screen と密着したデーモンみたいに使えるので、そこの中で DRb サーバをたちあげ、随時リクエストを実行していくようにします。(ただし :source で終了してくれたりはしないので、自分で過去の自分を kill するようにしています)

#!/usr/bin/env ruby

require "drb/drb"

DRb.start_service

o = DRbObject.new(nil, "druby://localhost:9999")
o.add_queue(ENV.to_hash, ARGV.join(" "))

こんな感じでリクエストをなげると、実行されて、一行ずつ screen の backtick エリアに表示されます。実際実行されているかどうかは、ps aux | grep AsyncDRb とかやると、wating... とか実行中のコマンドとかが表示されます。


でもって、これを有効にしたうえで、~/.gitconfig に alias を書きます。

[alias]
	ci      = !sh -c 'git commit -av && [ "$(git config --bool svn-remote.svn.autodcommit)" = "true" ] && $HOME/.screen/asyncrun.rb git svn dcommit && echo "dcommit done!"'
	svn-ad  = config svn-remote.svn.autodcommit true
	svn-ado = config svn-remote.svn.autodcommit false

自動的に dcommit したくない場合も十分あるので、svn-remote.svn.autodcommit の設定をみて、実行するかどうか決めています。

あとは普通に git ci とかやれば裏で dcommit が走りながら編集できるのでとても便利です。

2008年 07月 03日

iTerm つかう!!!

なんか、すでに理由を忘れたんだけど Terminal.app に辟易したので iTerm つかう。今までなんで iTerm を使っていなかったかっていうと

  • タブうざイ☆ (screen のほうがスクリプタブルなのでいい)
  • Cmd+Num は window 切り替えにしたい
    • window サイズをかえていくつか重ねてつかいたいので

ambi-width がどうとかのパッチはさておき、Cmd+Num をタブきりかえじゃなくて window きりかえにするパッチをかいた

diff --git a/PTYWindow.m b/PTYWindow.m
index ce2676f..8207d01 100644
--- a/PTYWindow.m
+++ b/PTYWindow.m
@@ -109,14 +109,33 @@
 - (void)sendEvent:(NSEvent *)event
 {
 	// NSLog(@"%s: 0x%x", __PRETTY_FUNCTION__, self);
-	
+
 	if([event type] == NSMouseEntered)
-	{		
+	{
         //NSLog(@"window mouse entered");
 		if([[PreferencePanel sharedInstance] focusFollowsMouse])
 			[self makeKeyWindow];
 	}
-	
+	if([event type] == NSKeyDown)
+	{
+		if ([event modifierFlags] & NSCommandKeyMask)
+		{
+			NSString* c = [event charactersIgnoringModifiers];
+			NSArray* windows = [[iTermController sharedInstance] terminals];
+
+			NSLog(@"pressed: Command+%s", c);
+			for (int i = 1, len = [windows count]; i <= len; i++)
+			{
+				if ([c isEqualToString: [NSString stringWithFormat: @"%d", i]])
+				{
+					// [[iTermController sharedInstance] terminalWindows]
+					[[[windows objectAtIndex: i - 1] window] makeKeyAndOrderFront: self];
+					break;
+				}
+			}
+		}
+	}
+
 	if (super) [super sendEvent:event];
 }
 
diff --git a/iTermApplicationDelegate.m b/iTermApplicationDelegate.m
index 648f46a..5bb6f4c 100644
--- a/iTermApplicationDelegate.m
+++ b/iTermApplicationDelegate.m
@@ -558,15 +558,16 @@ static BOOL usingAutoLaunchScript = NO;
 	while ((aTabViewItem = [enumerator nextObject])) {
 		aSession = [aTabViewItem identifier];
         NSMenuItem *aMenuItem;
-		
-        if(i < 10)
-        {
-            aMenuItem  = [[NSMenuItem alloc] initWithTitle: [aSession name] action: @selector(selectSessionAtIndexAction:) keyEquivalent: [NSString stringWithFormat: @"%d", i]];
-            [aMenuItem setTag: i-1];
-			
-            [aMenu addItem: aMenuItem];
-            [aMenuItem release];
-        }
+
+		if(i < 10)
+		{
+		//	aMenuItem  = [[NSMenuItem alloc] initWithTitle: [aSession name] action: @selector(selectSessionAtIndexAction:) keyEquivalent: [NSString stringWithFormat: @"%d", i]];
+			aMenuItem  = [[NSMenuItem alloc] initWithTitle: [aSession name] action: @selector(selectSessionAtIndexAction:) keyEquivalent: @""];
+			[aMenuItem setTag: i-1];
+
+			[aMenu addItem: aMenuItem];
+			[aMenuItem release];
+		}
 		i++;
 	}
 

設定は http://f.hatena.ne.jp/cho45/iTerm/ においてみた。一枚ずつのページをすすめながら設定するとべんりっぽい。ふつうに ~/Library/Preferences/net.sourceforge.iTerm.plist (ソースからコンパイルすると iTerm.plist じゃなくなるっぽい) でもいい気がするけど


あと、そもそも cvs head は Xcode 2.5 + Mac OS X 10.4.11 ではビルドできなくて、 iTermSecurityMgr.{m,h} と Shells.{m,h} を iTerm.framework に追加しないとだめだった。

なんか iTerm の window 順がいつのまにか変わることがあって Cmd+num がズレる……

フルスクリーンにするとズレるみたいだ。フルスクリーンにするなら新しいウィンドウつくろう

screen と vim の憂鬱

screen でいっぱい window つくって vim 立ち上げまくったりしていると、よく「スワップが残ってるよ、既にどっかで vim ひらいてるよ」と怒られるのです。しかしながら僕は C-z でバックグランドに飛ばしまくるので、どこの window でうごいているのか頑張らないとわかりません。僕は頑張りたくありません。とても困りました。だから僕は、それを解決する方法を考えました。

まずは、pid から、そのプロセスがどこの window で動いているかを知るために、以下のようなことをします。

# .zshrc

# window num -> tty の対応ができるように
tty > /tmp/screen-tty-$WINDOW
# pid2screen
#!/usr/bin/env ruby

require "pathname"

pid = ARGV[0]
tty = `/bin/ps -o tty= -p #{pid}`.strip
win = Pathname.glob("/tmp/screen-tty-*").find {|i| i.read[tty] }
if tty.empty? || !win
	exit 1
else
	puts win.to_s[/\d+$/]
	exit 0
end

これで、pid2screen [pid] すると pid から該当のウィンドウ番号が表示されるようになります。(なければ exit 1)

そして、vim のラッパを書きます。すこし長いのでレポジトリに直接リンクをはりますね。

内容的には

  • swp ファイルがあったら、それを読んで pid を抜きだす
  • その pid で pid2screen する。
  • 結果があるならその window にとばす

という感じです。window に飛ばしてさらにそのファイルをアクティブにしたいところですが、いい方法が浮かびませんでした。悲しいです。でも、とても便利になりました。嬉しいです。

prompt にゆのっちを表示する。(挫折)

いよいよ、ひだまりスケッチ×365がはじまるので、プロンプトを、よりゆのっちにしたいと思い、

 / _ / × <

としたかったのですが、挫折しました。

まず、iTerm に ambiwidth を正常に表示するパッチをあててビルドしました。この時点で基本的にちゃんと表示されるようにはなりました。でも screen で画面を切替えてると幅がずれることがあってダメでした。

最初 iTerm の疑ってデバッグをしていたのですが、どうやらエスケープシーケンスの時点でずれた値がくることがわかりました。どうやら screen のせいみたいでした。なので、screen に cjkwidth を認識するパッチをあててビルドしてみたのですが、これがうまくいきませんでした。壮絶に崩れまくったので諦めました。悲しいです。

Mac で screen をつかいつつ、ambiwidth な文字をちゃんと表示できているという人がいたら、ぜひ教えていただきたいです。

ちなみに結局妥協した結果

 / _ / X <

にしました。Osaka の × は綺麗な × なので、やっぱり × にしたいです。

2008年 06月 30日

どこにリンクされているのか、それっぽくわかるようにする

	def title(url)
		uri = URI(url)
		@ua.get(uri)

		text = ""
		case
		when uri.fragment
			fragment =  @ua.page.root.at("//*[@name = '#{uri.fragment}']") || @ua.page.root.at("//*[@id = '#{uri.fragment}']")

			text = fragment.inner_text + fragment.following.text + fragment.parent.following.text
		when uri.to_s =~ %r|^http://h.hatena.ne.jp/[^/]+/\d+|
			text = @ua.page.root.at("#main .entries .entry .list-body div.body").inner_text
		else
			text = @ua.page.root.at("//title").inner_text
		end
		text.gsub!(/\s+/, " ")
		text.strip!
		NKF.nkf("-w", text).split(//)[0..30].join
	rescue Exception => e
		@log.debug ["title:", e.inspect]
		""
	end

はてスタ IRC ストリームつかってて、URL だけだとどこにスターついたのかよくわからないので、こういうのをカマして、リンクされた周辺の文字列をひろっています。結構うまいこといきます。@ua は WWW::Mechanize で @log は Logger です。

ハイクを特別扱いしてるのは、はてスタ IRC ストリームのチャンネルで発言するとハイクに投稿されるようになっているため、ちょっと優遇してる感じです。