2009年 10月 26日

gerry++

2009年 10月 22日

JSDeferred 0.3.0 リリース

一年ぶりぐらいにリリース http://github.com/cho45/jsdeferred/tree/release-0.3.0

Deferred.connect

を API を変えてとりこんだものです。コールバックを引数にとる任意の関数を Deferred を返す関数に変更できます。

var timeout = Deferred.connect(setTimeout, { target: window, ok: 0 });
timeout(1).next(function () {
  alert('after 1 sec');
});

Deferred#retry

これもセコンさんが実装していたやつをだいたいそのまんま入れたものです。

Deferred.retry(3, function () {
  return http.get(...);
}, { wait: 1 }).
next(function (res) {
  res // 成功したらこっち
}).
error(function (e) {
  e // 3回リトライしてもダメなときはエラー
});

Deferred.onerror

これもセコンさんが(ry

Deferred 処理中に発生した例外を全部キャッチすることができます。デバッグに便利です。デフォルトでは undefined なので、適当な関数をつっこんでください。この関数によってエラーをキャンセルしたりとかはできません。

Deferred.onerror = function (e) { if (window.console) console.log(e) };

jQuery binding への変更

これもセ(r

$.fn.deferred が追加で定義されるようになり、コールバックをとる任意の jQuery メソッドを Deferred を使うようにできます。今までは $.ajax 系のみが Deferred を返すように jQuery をいじっていましたが、このメソッドは汎用性が高くなっています。

var click = $('div').deferred('click');
loop(10, function (i) {
    console.log(i);
    return click;
});

とかやるとステップ実行が実装できそうですね (試してない)

binding/googlechrome-extensions.js

セ(rさんが作ってたやつです。http://subtech.g.hatena.ne.jp/secondlife/20091013/1255435673

2009年 10月 17日

Android のビューで活用すると便利な select 要素

Android では select をクリックすると選択肢がいい感じにポップアップしてくれ見た目を大幅に変えることができそれによりアクションを選択するような場面で場所をとらずに有効に使うことができる以下例

フォーカス (オレンジの outline) がついているのが select である。クリックすると

というふうにポップアップされる。ここで、したい操作を選ぶ、という感じになる。もちろん選択肢が増えても適切にスクロールバーがでるため問題ない。

上の例に関係する部分の CSS と JS をぬきだす。ようは onchange イベントを監視しているだけでポイントというほどのところではないけど selectedIndex は代入できセレクトされているものを変更できるので戻しているところか

form select.post {
        position: absolute;
        top: 0;
        right: 5px;
        width: 25px;
        height: 24px;
        margin: 7px 0;;
        padding: 0 5px;
        outline: none;
        border: 1px solid #000;
        background: #999;
        color: #fff;
        text-align: center;
        -webkit-appearance: none;
}
new function PostOperations () {
    var define = {
        Post : function (form) {
            form.submit();
        },
        Location : function (form, input) {
            // do
        }
    };

    var form = document.getElementById("input");
    if (form) {
        var input = form.querySelector("input[name=msg]");
        var select = document.querySelector("select.post");
        select.addEventListener("change", function (e) {
            var fun = define[select.value];
            try {
                if (fun) fun(form, input);
            } catch (e) { alert(e) }
            select.selectedIndex = 0;
        }, false);
    }
};
2009年 10月 14日

gerry++

腹痛ではないが下痢ってる。アルコールのせいかな

2009年 10月 12日

gemcutter

net-irc を gemcutter にうつしてリリースした http://gemcutter.org/gems/net-irc 。RubyForge からの migration が

gem migrate net-irc

だけでできて感動した。単に RubyForge から gem もってきて push しているだけっぽいけど

でもって、net-irc の Rakefile を修正し、リリース

Rake::ShipitTask.new do |s|
	s.ChangeVersion "lib/net/irc.rb", "VERSION"
	s.Commit
	s.Task :clean, :package, :upload_doc
	s.Step.new {
	}.and {
		system("gem", "push", "pkg/net-irc-#{VERS}.gem")
	}
	s.Tag
	s.Twitter
end

shipit.rb に gemcutter のステップを定義してないので手動実行させてる。shipit.rb は git に対応しているので (いつのまに! っておもったら自分でやってた……)、元 (svn) のタスク定義からそんなに変わってはいない。ただし push まではしないので、自分で git push --tags, git push とかやる必要がある。ステップ定義すればいいんだけど、ぼくは確認してから push したいので定義してない。

gemcutter 側の Links とかに表示されているやつは gemspec にデータになっている。ちょっと残念なのは、RDoc のホストはやってくれないところだ。正直 gemcutter の説明だけで「この gem は使えそうだ」という判断はできない。CPAN みたいになるといいなぁ

2009年 10月 08日

Levenshtein 距離 / Ruby / RubyInline

module Levenshtein
	def levenshtein(a, b)
		if Inline::USABLE
			Inline.levenshtein(a, b)
		else
			PureRuby.levenshtein(a, b)
		end
	end

	module PureRuby
		def levenshtein(a, b)
			case
			when a.empty?
				b.length
			when b.empty?
				a.length
			else
				d = Array.new(a.length + 1) { |s|
					Array.new(b.length + 1, 0)
				}

				(0..a.length).each do |i|
					d[i][0] = i
				end

				(0..b.length).each do |j|
					d[0][j] = j
				end

				(1..a.length).each do |i|
					(1..b.length).each do |j|
						cost = (a[i - 1] == b[j - 1]) ? 0 : 1
						d[i][j] = [
							d[i-1][j  ] + 1,
							d[i  ][j-1] + 1,
							d[i-1][j-1] + cost
						].min
					end
				end

				d[a.length][b.length]
			end
		end

		module_function :levenshtein
	end

	module Inline
		begin
			require "rubygems"
			require "inline" # sudo gem install RubyInline

			inline do |builder|
				builder.c <<-'EOS'
					VALUE levenshtein (VALUE array1, VALUE array2) {
						VALUE ret;
						long len1 = RARRAY(array1)->len;
						long len2 = RARRAY(array2)->len;
						long i, j;

						long** d = ALLOC_N(long*, len1 + 1);
						for (i = 0; i <= len1; i++) {
							d[i] = ALLOC_N(long, len2 + 1);
							memset(d[i], 0, sizeof(d[i]));
						}

						for (i = 1; i <= len1; i++) d[i][0] = i;
						for (j = 1; j <= len2; j++) d[0][j] = j;
						for (i = 1; i <= len1; ++i) {
							for (j = 1; j <= len2; ++j) {
								int del = d[i-1][j  ] + 1;
								int ins = d[i  ][j-1] + 1;
								int sub = d[i-1][j-1] + (
									rb_equal(
										RARRAY(array1)->ptr[i-1],
										RARRAY(array2)->ptr[j-1]
									) ? 0 : 1
								);

								d[i][j] =
									(del <= ins && del <= sub) ? del:
									(ins <= del && ins <= sub) ? ins:
									sub;
							}
						}

						ret = LONG2FIX(d[len1][len2]);

						for (i = 0; i < len1; i++) free(d[i]);
						free(d);

						return ret;
					}
				EOS
			end

			module_function :levenshtein

			USABLE = true
		rescue LoadError
			USABLE = false
		end
	end
end


require 'rubygems'
require 'spec'

[Levenshtein::Inline, Levenshtein::PureRuby].each do |m|
	describe m do
		it "should return correct levenshtein distance" do
			[
				["kitten", "sitting", 3],
				["foo", "foo", 0],
				["", "", 0],
				["foO", "foo", 1],
				["", "foo", 3],
			].each do |a, b, expected|
				m.levenshtein(a.split(//), b.split(//)).should == expected
				m.levenshtein(b.split(//), a.split(//)).should == expected
			end
		end
	end
end

Spec::Runner.run

require "benchmark"

Benchmark.bm(10) do |x|
	n = 10000

	x.report("inline") do
		n.times {
			Levenshtein::Inline.levenshtein("kitten".split(//), "sitting".split(//))
		}
	end if Levenshtein::Inline::USABLE

	x.report("pureruby") do
		n.times {
			Levenshtein::PureRuby.levenshtein("kitten".split(//), "sitting".split(//))
		}
	end
end

なんか度々使うたびに移植している気がするのでメモ

結構 Ruby でやると遅いので RubyInline でも書いてみた。

ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]
                user     system      total        real
inline      0.330000   0.010000   0.340000 (  0.346537)
pureruby    2.500000   0.020000   2.520000 (  2.596968)

1.9 にすれば pureruby でも 1.8 の pureruby の倍ぐらいにはなる。

net-irc をgithub に移動。2ig.rb (2ch IRC Gateway)

割とちょこちょこ触ることが多く、svn だと不便なので、http://github.com/cho45/net-irc に移動した

でもって要望があって気がむいたので 2ig.rb というものを作ってみた。

ruby example/2ig.rb --debug

で起動する

実況スレみるにはいいかもしれない。(実況系の板でしか試してないのでほかの板だとへんかも)

  • dat 取得 interval はデフォルトで90秒、トピックのスレアドレスに後に数字をいれると設定できる
  • AA っぽいものは tinyurl に投げて URL 化している。
    • 改行が多く、記号の割合が多いものを AA と判断 (aa? メソッド)
  • 1000 までいくと次スレを推測して提示する
    • 提示されたやつを /topic url.. すればチャンネルそのままで継続
    • 現スレのスレタイの数字+1が含まれるスレッド、直近にアドレスが投稿されたスレッド、編集距離を見て判断
      • 可能性が非常に高いスレッドが見つかった場合は色つき
  • post 機能は実装してないのでだれかつくってほしい


2009年 10月 07日

Android の geolocation で GPS を使う方法

Android には Gears が入っていて、gears の geolocation API が使えることは使えるのだけど、GPSをちゃんと使う方法がなかなかわからなかったのでメモ (素朴に使うと WiFi + IP アドレス?しか使わないっぽくて、とれないことが多い)

var geo = navigator.geolocation || google.gears.factory.create('beta.geolocation');

geo.getCurrentPosition(
        function (pos) {
                alert([pos.coords.latitude, pos.coords.longitude].join(','));
        },
        function (e) {
                alert(e.message);
        },
        {
                enableHighAccuracy: true
        }
);

enableHighAccuracy が true じゃないと GPS 情報を使わないようだ。このままだと Google の Geoloation API をまだ叩きにいく。さらに gearsLocationProviderUrls: [] とか指定すれば、純粋にGPSだけの情報がとれるようだ。

        {
                enableHighAccuracy: true,
                gearsLocationProviderUrls: []
        }

ただ、なんか GPS のタイムアウト時間が短いので、前もって別のアプリケーションで GPS fix させてからじゃないとダメな気がする。要検証。watchPosition でとれるまで待つという方法もあるかもしれない

追加検証してみたけど、ちゃんと座標を取得したいなら、watchPosition でとれるまで待って、clearWatch するのがよさそう (キャンセルボタンもつけて)。

watchPosition ならとれるまで試行できる。

gearsLocationProviderUrls: [] を指定すると Chrome でエラーがでるようになったようだ。なのでつけないほいがいい、ないしブラウザ判定を入れる必要がある

2009年 10月 05日

gerry++

2009年 09月 30日

todo: ブックマークレットの token 処理についてあとで書く