2007年 11月 03日

GranParadiso

Firebug1.1.0b7 を入れた。うごいている。しかしいままで以上に重い。GranParadiso の重さと相乗効果

はてなのマルチログイン uc.js がないと激しく不便なのでデバッグして Fx3 向けに修正した。tumblr のやつも同じ修正が必要だけど、あんま使ってないのでそのうちでいいや。

http://coderepos.org/share/changeset/1051

ekfloras (blosxom clone) のプラグイン機構

とりあえず module/pluggable を使っていたものの、別にリロードとかできなくていい (CGI なので使い捨て) ので、もっと簡単なプラグインシステムにした。ついでに rubygems への依存もなくなったので、純粋に Ruby がインストールされていればレポジトリからチェックアウトしてすぐ動くようになった。

# プラグインファイルをロードして
# インスタンスの配列を実行すべき順にソートして返す
def load_plugins
	Pathname.glob(@opts[:plugin_dir] + "/**/*.rb") do |f|
		load f
	end
	pm = self.class.const_get(:Plugin)
	pm.constants.inject([]) {|r,i|
		r << pm.const_get(i).new(self)
	}.sort_by {|i|
		i.priority
	}
end

みたいにして Ekfloras::Plugin::* をプラグインとみなし、メソッドをよぶ。かなり簡単だけど普通は十分なプラグイン機構だと思う。(ファイル名とクラス名間の名前の制約がなくなるので、てきとーにプラグインんが書けるようになる。逆にこれのせいでリロードとかが実装できない) こんだけ単純だと特にライブラリする必要もないので欲しくなったときに書きなぐればいいのがいい。

(priority メソッドの返り値でソートしているのは、プラグイン同士で実行順に気をつかうようにするため。blosxom だと名前順になっているから、各ユーザがプラグインの仕組みを理解してファイル名を書きかえたりしないといけなかったけど、これでだいたいてきとーにプラグインディレクトリにほうりこんでも動くようになるはず)

ちゃんとインスタンス化してメソッドをよぶようになるので、各プラグイン内のメソッドの名前がコンフリクトしたり、データが共有されることがなくていい。ちゃんとメソッド間ではデータ共有できるし (こんなのあたりまえなんだけど、Ruby 系のプラグイン機構ってこういうあたりまえのことができないのが多いイメージ)

あとは設定かな。設定はできるだけ書きたくないから (blosxom のプラグインのいいところは投げこめば動くことだと思う)、最小設定と、オプションをいくつかとかになるかな。

ekfloras の速度

  1. #<Ekfloras::Plugin::Meta:0xb7bcc03c>
  2. #<Ekfloras::Plugin::Paging:0xb7bcbfec>
  3. #<Ekfloras::Plugin::Comment:0xb7bcc014>

(EntriesCache なし)

ruby 1.8.4
1159 files, 1.212316 sec
1159 files, 1.722761 sec
1159 files, 1.390812 sec
1159 files, 1.353448 sec
1159 files, 1.423345 sec

ruby 1.9.0 (2007-09-24 patchlevel 0) [i686-linux]
1159 files, 1.025227 sec
1159 files, 1.075664 sec
1159 files, 1.202309 sec
1159 files, 1.300395 sec
1159 files, 1.284752 sec
  1. #<Ekfloras::Plugin::EntriesCache:0xb7c08ae8>
ruby 1.8.4
1159 files, 0.733907 sec
1159 files, 1.166876 sec
1159 files, 1.437776 sec
1159 files, 0.857321 sec
1159 files, 0.906379 sec

ruby 1.9.0 (2007-09-24 patchlevel 0) [i686-linux]
1159 files, 0.315943 sec
1159 files, 0.301194 sec
1159 files, 0.446674 sec
1159 files, 0.254224 sec
1159 files, 0.45789 sec

EntriesCache (エントリをオブジェクト化した状態でマーシャライズしてキャッシュするプラグイン) 入れた ruby1.9 がかなりはやい。

2007年 11月 01日

blosxom clone ekfloras

http://coderepos.org/share/browser/lang/ruby/ekfloras

Ruby で blosxom クローンを書いてみた。他の言語と違って blosxom のプラグインの雰囲気をのこしたり、まじめに使うことをちょっと考えたりするように。

テンプレートエンジンに ERB と Erubis どっちもサポートしようとおもって書いていたけど、途中でテンプレートが Erubis 専用になってしまっていることに気付いてめんどくなったのでどっちもやめて (イミフ) 前書いた MiniERB をつかうようにした。(空白圧縮とかしないけど ERB の 2.5 倍ぐらいはやい)

いまどきブログ CGI なんて書いてもだれもつかわないんだけどなぁ。ふつうにどっかのサービスつかったほうがいい。でも blosxom 系のはブログつーるというより他の使いかたをしやすいからおもしろい。

最近の blosxom クローンのまとめ

最近の、であって、僕が書いた、じゃないですよ? (coderepos にあるもののみ)

(使えそうな順にならんでます)

Lua は付属インタプリタでは実装できない。PHP と Python はやりたいと思わない。VBScript は Windows で CGI 環境つくるのがめんどうくさい。

Haskell と C# (on mono) あたりはやってみたいけど、ここで「やってみたい」とか書いている限りやらないだろう。本当にやってみたいなら既にやっているはずだ。

2007年 10月 31日

Perl memo

mobirc で Perl の学習をする日

検索用 -> 変数に入れたメソッド名・サブルーチン名でサブルーチンを呼びだす Ruby send

$self->$method_name;

{
    no strict 'refs';
    &$method_name();
}

__PACKAGE__->can($method_name)->();

POE と Perl の基本メモ

KERNELとかはPOEがエクスポートしてる関数。@array[1,2,3] とかは配列のスライス。ハッシュ版は @hash{'key1', 'key2'}

http://twitter.com/typester/statuses/375208972

@_[KERNEL, SESSION] とかが全く読めなくてこまったんだぜ! (そして最終的に使わなかった)

n行で書くテンプレートエンジン

io のやつは 50 行ぐらいだった。いい線じゃないですか!! 正規表現ないのに!!! (あるけど標準じゃない)

とかはどうでもよくて、Ruby で 40 行で ERB に似てるテンプレートエンジン (たぶんすごいだめなバグがある気がする)

class MiniERB
	module Util
		ESCAPES = { "&" => "&amp;", "<" => "&lt;" , ">" => "&gt;"}

		def escape(str)
			str.to_s.gsub(/[#{ESCAPES.keys.join}]/o) {|i| ESCAPES[i] }
		end
		module_function :escape
	end

	attr_reader :src

	def initialize(template)
		@src = compile(template)
	end

	def compile(s)
		ret = "_tt_string = ''¥n"
		while m = s.match(/<%([^¥s]*)/)
			ret << "_tt_string << #{m.pre_match.dump}¥n"
			code = m.post_match.match(/(.*?)%>/)
			s = code.post_match
			code = code[1]
			case m[1]
			when "h"
				ret << "_tt_string << MiniERB::Util.escape(#{code})¥n"
			when "="
				ret << "_tt_string << (#{code}).to_s¥n"
			else
				ret << "#{code}¥n"
			end
		end
		ret << "_tt_string << #{s.dump}"
		ret
	end

	def result(binding=TOPLEVEL_BINDING)
		eval(@src, binding)
	end
end

sample

require "time"

entries = [
	{
		:title => "hoge<<<&>>>>>>>>>>",
		:body => "a<a href='aa'>a</a>a",
		:date => Time.now
	},
	{
		:title => "fuaaaaa<<<",
		:body => "hehe, Nice boat.",
		:date => Time.now
	},
]

puts MiniERB.new(DATA.read).result(binding)

__END__
<% entries.each do |entry| %>
<div class="entry">
	<h2><%h entry[:title] %></h2>
	<div class="body"><%= entry[:body] %></div>
	<div class="info">Time: <%= entry[:date].xmlschema %></div>
</div>
<% end %>

%h で htmlescape 済みの。 = で生のデータうめこみ。

できるだけ式展開するバージョン (早くなると思われる) 同じく 40 行

class MiniERBExpressionExpantion
	module Util
		ESCAPES = { "&" => "&amp;", "<" => "&lt;" , ">" => "&gt;"}

		def escape(str)
			str.to_s.gsub(/[#{ESCAPES.keys.join}]/o) {|i| ESCAPES[i] }
		end
		module_function :escape
	end

	attr_reader :src

	def initialize(template)
		@src = compile(template)
	end

	def compile(s)
		ret = "_tt_string = ¥""
		while m = s.match(/<%([^¥s]*)/)
			ret << "¥#{#{m.pre_match.dump}}"
			code = m.post_match.match(/(.*?)%>/)
			s = code.post_match
			code = code[1]
			case m[1]
			when "h"
				ret << #{MiniERB::Util.escape(#{code})}"
			when "="
				ret << #{(#{code}).to_s}"
			else
				ret << "¥"¥n#{code}¥n _tt_string << ¥""
			end
		end
		ret << "¥" << #{s.dump}"
		ret
	end

	def result(binding=TOPLEVEL_BINDING)
		eval(@src, binding)
	end
end


ベンチマーク

require "time"
data = DATA.read
entries = [
	{
		:title => "hoge<<<&>>>>>>>>>>",
		:body => "a<a href='aa'>a</a>a",
		:date => Time.now
	},
	{
		:title => "fuaaaaa<<<",
		:body => "hehe, Nice boat.",
		:date => Time.now
	},
]

require "benchmark"
Benchmark.bmbm do |x|
	x.report("concat") { 100000.times {
		MiniERB.new(data).result(binding)
	} }
	x.report("expexp") { 100000.times {
		MiniERBExpressionExpantion.new(data).result(binding)
	} }
end

__END__
<% entries.each do |entry| %>
<div class="entry">
	<h2><%h entry[:title] %></h2>
	<div class="body"><%= entry[:body] %></div>
	<div class="info">Time: <%= entry[:date].xmlschema %></div>
</div>
<% end %>

Mac 1.8.2 / 1.83GHz CoreDuo

ruby 1.8.2 (2004-12-25) [universal-darwin8.0]
Rehearsal ------------------------------------------
concat  35.230000   0.240000  35.470000 ( 36.472992)
expexp  33.690000   0.220000  33.910000 ( 35.208808)
-------------------------------- total: 69.380000sec

             user     system      total        real
concat  35.220000   0.210000  35.430000 ( 36.348813)
expexp  33.570000   0.180000  33.750000 ( 34.598484)

ubuntu LTS 1.8.4 / Intel(R) Celeron(R) CPU 2.66GHz

ruby 1.8.4 (2005-12-24) [i486-linux]
Rehearsal ------------------------------------------
concat  37.250000   1.040000  38.290000 ( 38.371623)
expexp  36.270000   1.000000  37.270000 ( 42.917439)
-------------------------------- total: 75.560000sec

             user     system      total        real
concat  37.110000   0.980000  38.090000 ( 38.109001)
expexp  35.650000   1.030000  36.680000 ( 36.799591)

ubuntu LTS 1.9.0

ruby 1.9.0 (2007-09-24 patchlevel 0) [i686-linux]
Rehearsal ------------------------------------------
concat  37.910000   0.060000  37.970000 ( 38.100921)
expexp  35.910000   0.010000  35.920000 ( 35.944483)
-------------------------------- total: 73.890000sec

             user     system      total        real
concat  37.870000   0.010000  37.880000 ( 37.909874)
expexp  36.040000   0.020000  36.060000 ( 37.622068)

>

いちおうはやくなったようだ。誤差の範囲すぎる。

(あたまわるくて意味ないベンチした)

シュワルツ変換

http://en.wikipedia.org/wiki/Schwartzian_transform

覚えた! map sort map みたいなのをいうらしい。やってたけど名前を知らなかったw

Ruby だと sort_by になるからやらなくていい。

スペルが全く覚えられない感じだけど、幼女が「しゅわるちゅへんかんー」とか言ったら萌えるんじゃないか、と一瞬思った。「だけど」の前と繋がってないな。

2007年 10月 30日

Chemr + Amalgam

Chemr でいっぱい開いてると、目的のやつにたどりつくまでめんどいので、Amalgam から検索できるようにしてみた。DRb++

http://screencast.com/t/9jJng5yu7U

http://lab.lowreal.net/test/chemr/Chemr.2007-10-30.dmg (設定可能版の Chemr)

  • http://svn.coderepos.org/share/lang/ruby/Amalgam/user-plugins/chemr.rb を ~/.amalgam/plugins に
  • うえのファイルの =begin ~/.chemr/initrc.rb から =end までを ~/.chemr/initrc.rb に追記
  • Chemr 再起動してファイルたくさんひらく
  • .d r "Chemr" (プラグインのトップレベル候補をリロード)
    • 機能のバージョンだとできないので touch ~/.amalgam/plugins/chemr.rb して .d reload_plugins
  • chm ファイル名でトップレベルに候補追加
  • 選択して RET するとサブランチャに (初回は Chemr から index をとってくるのでおもい)
  • さらに選択して RET すると該当のウィンドウがアクティブになって項目を表示

libruby ふくめてないからそのままだと落ちると思う。ぜんぶにふくめたほうがいいのだろうなぁ……

ローカル gem の chm バンドルをつくる ruby スクリプト

http://coderepos.org/share/browser/lang/ruby/misc/chm-generators/generate-local-gems-chm-bundle.rb

カレントディレクトリに GemRdoc.chm というバンドル (Chemr だけで読める) をつくる (ただしテンプレに Resh をつかっていること前提)。ドキュメントの実体は gempath にあるほうなので、gem install したり削除したりしたら実行しなおす。バンドルみたいにただのディレクトリだと symlink とかつかえてよい。

AR とか AS のももちろんインデクスされるので、そういう系の .chm はいらなくなった。

.chm にコンパイルするのも yaml から変換すれば簡単にできるだろうけれど、時間かかるのでめんどそう