自分の興味の赴くままにIT技術系のネタを取りとめもなくメモっています。
Ruby言語やLinuxのネタが多いです。

January 01, 2006 [長年日記]

[Misc] あけましておめでとうございます

今年もよろしくお願いします。

[tDiary] 久々アップデート

気が付いたらアップデートもせずに1年経ってた。いやー、すごい放置ッぷりだったなぁ(苦笑)。

さてさて、ちゃんと動いてるかな?


January 02, 2006 [長年日記]

[Ruby] Moduleのネスト

よくやる間違いメモ。

module A
  module_function
  FOO = "foo"
  def foo; p FOO; end
  
  module B
    extend A  #(1)
    module_function
    def bar
      foo     #(2)
      p FOO   #(3)
    end
  end
end
A::B.bar

上記(1)でA::BはAをextendしないと(2)でエラーになるけど、(3)は使える。なんとなく、メソッド呼び出しもできちゃって良いような気がする、というかいつもそれで間違える。あ、でも、@@foo, @foo等の変数たちもextendしないとダメなのね。定数だけ例外、なのか・・・。


January 05, 2006 [長年日記]

[Ruby-GNOME2] udbm-gtk

UDBM(UPPAAL DBM Library)の、グラフィカルビューアとしてRuby/GTK2を使って頂いています。irbから操作しているみたいなので、実際はウィジェットライブラリみたいな感じなのかな。きれいなグラフが書けるみたいです。


January 06, 2006 [長年日記]

[Ruby-GNOME2] 困った質問

例えば、Widgetの使い方の質問があったとき、「なんでそうなってるの?」という質問をされると非常に答えに窮する。

なぜならば、オレはWidgetに対しRubyからアクセスする部分(つまりWrapper)については実装しているけど、Widgetそのものを実装したわけではないため、そのWidgetがどうしてそのようなAPI/実装になっているかなんて背景までは知らないからだ。

結果、答えとしては「オリジナルのWidget作者に聞いてみてくれ。」ということになるのだが、オリジナルのWidget作者に質問するためにはRubyのコードではなく、Cのコードで書き直して例示しないと作者の人は困ってしまうだろう(その人もRuby-GNOME2を使ってくれているのなら話は別だけど...)。

これって、Ruby-GNOME2のように、オリジナルのもののバインディング、というもののウィークポイントだよね。質問者もフラストレーションが溜まるだろうと言うことは容易に想像できる。

それに対し、ではどうすれば解決できるかというと、オリジナルのライブラリとRuby-GNOME2の両方に精通した人が現れる、ということだ。

え?オレ?ムリムリ、だって量多すぎるんだもん。

オレができるのはせいぜいC版とRuby版の両方を作り比べてみて、その両者の動きが違うようであればRuby側のバグとして改修して、それ以外の時は「GTK+でも同じだねー」という確認を取るところまでだね。

[Misc] ケースバイケース

上記の話の流れで、「ウインドウサイズを(デフォルト状態から)変更できなくするのはUIデザインとしてどうよ?」という質問があったので「ケースバイケースじゃないかと思うんだけど・・・。どうして?」と返したところ「画面が小さい場合に問題になるんじゃない。」という返事が。

でもさぁ、それこそケースバイケースなんだと思うんだけどなぁ。

例えば、PDAや携帯なんかをターゲットにするのか、PCをターゲットにするのか、PCにしたって、640x400の画面サイズからサポートするのか1024x768からをターゲットにするのか、それによって全然状況が違ってくるよね。

第一、小さい画面をサポートするのであれば、ウインドウサイズが変更できるかどうか、ということよりも別のこと(ボタンの配置自体の見直しなど)を考える必要があるはず。PCと携帯で同じボタンの配置が使えるわけないしね。

ちょっと話は変わっちゃうかもしれないんだけど...。

最近までWWWアプリを作る場合、JavaScriptを使うのはいろいろなWWWブラウザのことを考えるとよろしくない、というような風潮があったような気がしたけど、Ajaxが出てきたらそんなこと言う人いなくなっちゃったね。

もちろん、機が熟したってこともあるんだろうけど、「せめてw3mでは使えるようにしとこーよー」とか言う人はだいぶ減ってしまったような気がする。

いや、別にそれが悪いこととは思って無くてむしろ肯定派なんだよね。

つまり、結局はそのアプリの作り手が、そのアプリがどういったユーザを対象にするのか適切に考えているかってことが大切なんだよね。w3mもターゲットにするんであればそのように作れば良いんだし、極端な話、IE特化でも良いわけだし。

・・・ってか、case by caseって英語の使い方、間違ってるような気がしてきた。It depends.とかって言った方が良かったのかな・・・ダメだこりゃorz。


January 07, 2006 [長年日記]

[Ruby] Ruby-GetText-Packageサンプルブログ特集(笑)

Ruby-GetText-Packageのギリシャ語翻訳がようやく届いた。これで、10言語(一部12言語)をサポートできたことになる。

そこで記念にRuby-GetText-Packageに含まれるRuby on Railsを使ったサンプルブログアプリケーションの画像イメージを取ってみた。全部同じエラー画面ね。ちょっと大きいけどご容赦を。

まずはen(英語)。まぁ、見慣れてるって言えば見慣れてるかな。

英語版サンプルブログ

次はja(日本語)。わかりやすい。(当たり前か)。

日本語版サンプルブログ

次からはロケールのアルファベット順、ということでまずはcs(Czech:チェコ語)。

チェコ語サンプルブログ

de(ドイツ語)。昔、ちょっと習った記憶がないこともないが、ほぼ読めず。

ドイツ語サンプルブログ

el(ギリシャ語)。ギリシャ語の字っておもしろい形をしてるね。

ギリシャ語サンプルブログ

es(スペイン語)。他に比べると英語に近いかなぁ。

スペイン語サンプルブログ

fr(フランス語)。これも読めんなー。

フランス語サンプルブログ

ko(韓国語)。ハングル自体は最近なじみが出てきたけど、やっぱり全く読めないや。

韓国語サンプルブログ

nl(オランダ語)。これも多少英語に近いか・・・。

オランダ語サンプルブログ

pt_BR(ポルトガル語(ブラジル))。これもなんとなく読める文字が多い気がする。< ホントか?

ポルトガル語(ブラジル)サンプルブログ


January 10, 2006 [長年日記]

[Misc] よたらぼ保管庫はじめました

むとぽん7別館をyotabanana.comに移設し、「よたらぼ保管庫」と名前を新たに再スタートすることにしました。左メニューのリンクから辿れます。

ようやく私が持っている個人的なコンテンツはyotabanana.comに集約できました。

実は去年中にはやっておこうと思っていたのですが、なんと一年かかってしまいました。ってか一年って早いですねー。

新しいコンテンツともどもよろしくお願いします。

[Misc] よたらぼ保管庫について

Hiki-0.8.5をベースに若干ソースコードやテンプレートをカスタマイズしています。

Wikiの特徴である画面の上面にあるアドミンボタン(アンカ)は、一部は左メニューへ、そして私のみが触るボタンについては右下に持って行きました。これで、Wikiだから誰でも編集できるだろーと思う人も減るんじゃないかと思います。よりCMSとして使うための改造、という感じですね。

また、サイドメニューなどのデザインもよたらぼ(よたばなな)に合わせてあります。なかなか統一感が出せてきたんじゃないかと思いますがいかがでしょう。


January 14, 2006 [長年日記]

[Ruby] Ruby-GetText-Package-1.1.1

リリースしました。バグフィクスとギリシャ語の追加です。

特にRuby on Rails周りの不具合を改修してますので、そちらでご利用の方はアップデートしてくださるようお願いします。


January 15, 2006 [長年日記]

[Ruby] Ruby on RailsでRuby-GetText-Packageを使う

ようやく日本語版チュートリアルをアップデートしました。Ruby on RailsでL10n化を考えている方はぜひご一読ください。

しっかし、英語と日本語の両方をメンテナンスするのはとてもつらいなー。むー。

[Hiki] オプションプラグイン-1.1

Hiki-0.8.5で動作するように改修しました。


January 16, 2006 [長年日記]

[Ruby] test/unitがうまくいかない

というレポートをRuby-GNOME2メーリングリストで受け取った。

調べてみたところ、なるほどなーと思ったのでここにメモっておく。

ここでは簡単なライブラリ"FooClient"というのを想定する。

このクラスは設計の良し悪しは別として(^^;)、requireした瞬間にFooサーバーに接続し、Rubyプログラムが終了するタイミングで接続を解除する仕様とする。

# fooclient.rb
module FooClient
  #require時にServerに接続してしまう
  @connected = true
  at_exit {
p "at_exit"
    #at_exit時に接続を解除
    @connected = false
  }
  module_function
  def connected?; @connected; end
end
 
if $0 == __FILE__
  p FooClient.connected?
end

んで、テスト

# test.rb
require 'test/unit'
require 'fooclient'
 
class FooClientTest < Test::Unit::TestCase
  def test_connected
    assert_equal true, FooClient.connected?
  end
end

んでもって、テストを実行してみると、テストが失敗してしまう(FooClient.connected?がfalseを返す)。FooClientでは確かにat_exitを使ってるのが気持ち悪いことは気持ち悪いけど、でも、テストプログラムのassert_equalの部分はまだat_exitを呼び出す前のような感じがするし・・・。
と、小一時間くらいは悩んでしまったわけだが、理由は簡単で、test/unit.rbが内部で以下のようなコードを持っているのが原因だ。

at_exit do
  unless $! || Test::Unit.run?
    exit Test::Unit::AutoRunner.run
  end
end

これは、test.rbにテストを実行するようなコードを一切書く必要が無いような配慮だ。プログラム終了をトリガーにしてTest::Unit::AutoRunner.runを実行する。

ちなみに、at_exitは以下のようなサンプルを実行するとその動きがわかると思う。

% cat at.rb
at_exit { p 1 }
at_exit { p 2 }
p 3
 
% ruby at.rb
3
2
1

つまり、at_exitに最後に登録したものから呼ばれることになる(2→1)。

さて、上の例の回避策をどうすれば良いかというと、上記のat_exitの順序を意識しつつ、requireの呼び出しをすればよい。おそらくほとんどの場合、require 'test/unit'を最後に呼び出すことになるだろう(つまり、他のライブラリのat_exitを呼び出す*前に*テストを実行する)。

test.rb
require 'fooclient'
require 'test/unit'
  :
  :

でもなぁ。requireの順番を気にするなんていう回避策はいただけないよなぁ、と言う場合のもう一つの回避策としては、Test::Unit::AutoRunner.runをtest.rb内で呼び出すという手がある。そうすれば、test/unit.rbのat_exitは関係なくなるからね。

test.rb
require 'test/unit'
require 'fooclient'
 
class FooClientTest < Test::Unit::TestCase
  def test_connected
    assert_equal true, FooClient.connected?
  end
end
exit Test::Unit::AutoRunner.run

まぁ、どっちもどっちか。でも、オレはこっちの方が好きかな。やっぱり、Rubyインタプリタが終了することをトリガーにメインのプログラム(テスト)を実行するってのはちょっとトリッキーすぎな気がする。

ともかく、自分が開発していないライブラリを混ぜてテストするときはここであげた点を意識しておいた方がいいだろうね。

at_exitに優先度つけれたら良いのかもしれないけど...。

本日のツッコミ(全4件) [ツッコミを入れる]

Before...

むとう [すみません、もう少し詳しくお願いしますm(__)m。]

なかだ [testrbというのは、まさにそのTest::Unit::AutoRunner.runを実行するためのスクリプトです..]

むとう [なるほど。そういうコマンドがあるんですね。 お恥ずかしながら知りませんでした。 ありがとうございます。]


January 20, 2006 [長年日記]

[Ruby] 保存しておいた文字列を高速に検索する

同じHTTPリファラから10回以上のアクセスがあった場合、それ以上(11回目から)のアクセスができないようにするためにはどのような実装が良いのかな。

まずは、リファラのURLをHashのキーとして持つことを考えてみた。

@deny_urls = Hash.new
def allow?(referer)
  ret = true
  if @deny_urls[referer]
    if @deny_urls[referer] >= 10
      ret = false
    else
      @deny_urls[referer] += 1
    end
  else
    @deny_urls[referer] = 1
  end
  ret
end
 
s = Time.now
(0...10000).each do |i|
  (0...15).each do
    allow?("http://www.yotabanana.com/#{i}")
    allow?("http://www.yahoo.com/#{i}")
    allow?("http://www.google.com/#{i}")
  end
end
p Time.now - s

なんだか、if文だらけになっちゃったし、使用例の部分もいまいちだけど、まぁいっか。

まぁ、ともかく、次に、keyに文字列のhashを使ってみる(ってかRubyのHashってこういう使い方しても大丈夫なんだっけ?(^^;))。

def allow?(referer)
  ret = true
  hash = referer.hash  #(1)
  if @deny_urls[hash]
    if @deny_urls[hash] >= 10
      ret = false
    else
      @deny_urls[hash] += 1
    end
  else
    @deny_urls[hash] = 1
  end
  ret
end

allow?メソッドのとこだけ書き換え。他は同じね。

で、両者の性能を測ると

% ruby test.rb
4.146538
% ruby test2.rb
3.488657

うーん、もうちょっと高速化したいなぁ。他に高速化する方法ってある?

ポイントとしては、以下の感じ。Rubyクイズスタート!応募はツッコミ欄で!!ちなみに応募しても何も出ません(ってオイ(^^;))

  1. (1)のような数値化はallow?メソッドの中で実行する必要がある。(ループの外には出せない)
  2. 別にデータ構造自体Hashでなくてもいい。
  3. だいたい(90%以上で良いかな)うまくいけばOK。
  4. データは不可逆でOK。

本日のツッコミ(全4件) [ツッコミを入れる]

Before...

眠くなってきたzunda [こんなのはどうでしょう? @deny_urls = Hash.new( 0 ) def allow?(refere..]

むとう [あ、ごめんなさい。&lt;と&gt;間違えて書いてました。直しておきました。 #手書きなんです・・・(苦笑)。]

むとう [うーん、すごい。 zundaさん案のif文を整理しただけでも高速化するんですね! % ruby test3.rb ..]


January 28, 2006 [長年日記]

[tDiary] アクセスカウンタ表示プラグイン-2.0

久しぶりに全面見直しをかけました。まともにいじるのは2002/10以来なので2年以上経ってしまいました(苦笑)。

(今のところ反映されていないようですが)明日あたりから こちらで最新版を取得できると思います。counter.rbを置き換えるだけで移行できますので、ぜひお試しください。

変更点ですが、内部的な安定性の向上、設定画面の追加、kiribanプラグインの追加です。

設定画面からは全ての項目の設定ができるようになりました。キリ番をちょこちょこ指定したい人は格段に使いやすくなったと思います。

#英語版を使う場合はen/counter.rbを一緒に入れてください。

また、kiribanプラグインですが、これは、設定画面で「全て」「今日」「それ以外」のメッセージを記述をしておくと、<%= kiriban %>と書いたところに適切に展開してくれるものです。メッセージを変更したい場合も、設定画面からすぐにできますので、ぜひ使ってみてください。うまく使えばきっと楽しいキリ番ライフになるんじゃないかと思います(?)。

あ、そうそう、デフォルトでかなりの種類のロボット系ユーザエージェントからのアクセスをカウントしないようにしました。導入後、急にアクセス数が減る感じがしてちょっとがっかりするかもしれません(私のところでも1/5程度に...orz)。

あとは内部的な話ですが、今回データをカウンタデータとアクセス元データの2つに分け、極力カウンタデータが壊れないようにしました。アクセス元データのサイズも以前に比べてかなり抑えることができたので全般的に負荷が軽くなったのではないかと思います。

[tDiary] リファラエディタプラグイン-1.2.2

設定画面でのカテゴリ表示を「リンク元」のところで表示するようにしました。

ちょっとした修正で現状不満のない方はアップデートする必要はありません。

[Hiki] ヘッダタイトル変更プラグイン

0.8.5で動作するように記述内容を変更しました。


January 31, 2006 [長年日記]

[Rails] パッチ2題

1つめは、ActiveRecordのvalidates_length_ofが日本語だとうまくいかない件。うまくいかないっていうか、結局バイト数になっちゃってるからそこを文字数でカウントしてあげるようにすれば良いだけなんだけどね。

(activate-x.x.x/lib/activerecord/validations.rbへのパッチね)

--- validations.rb.old  2006-01-31 02:22:42.000000000 +0900
+++ validations.rb      2006-01-31 02:41:03.000000000 +0900
@@ -459,7 +459,7 @@
           message = (options[:message] || options[message_options[option]]) % option_value
  
           validates_each(attrs, options) do |record, attr, value|
-            record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
+            record.errors.add(attr, message) unless !value.nil? and value.split(//).size.method(validity_checks[option])[option_value]
           end
         end
       end

あ、そうそう、どこかで$KCODEを指定しとかないとsplit(//)がうまく動作しないので念のため。

もう一つは、Content-Typeをアプリ側で指定してしまうとRJSが動作しなくなる件。例えば以下のような例。

class ApplicationController < ActionController::Base
  before_filter :set_charset
  def set_charset
    @headers["Content-Type"] = "text/html; charset=utf-8" 
  end
end

これって、.rjsなファイルだったら強制的にContent-Typeをtext/javascriptにしちゃえばいいと思うんだけど、それをせずにすでに指定された上記の"text/html; charset=utf-8"を使うのが問題だと思う。

なので、このパッチは.rjs/.rxmlの時は強制的にContent-Typeを書き換えるようにする。

以下は、SVN版のactionpack/lib/action_view/base.rbへのパッチ

--- base.rb.old 2006-01-31 02:05:28.000000000 +0900
+++ base.rb     2006-01-31 02:05:38.000000000 +0900
@@ -394,10 +394,10 @@
           body = case extension.to_sym
             when :rxml
               "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
-              "@controller.headers['Content-Type'] ||= 'text/xml'\n" +
+              "@controller.headers['Content-Type'] = 'text/xml'\n" +
               template
             when :rjs
-              "@controller.headers['Content-Type'] ||= 'text/javascript'\n" +
+              "@controller.headers['Content-Type'] = 'text/javascript'\n" +
               "update_page do |page|\n#{template}\nend"
           end
         else

で、これ、RailsのMLに投げたら、Trac使え、と言われたので言われたとおりにhttp://dev.rubyonrails.org/を見てみたところ、テストケース書け!って書いてある。ってか、正直書き方わかりません....ということで放置の方向で。あ、いや、誰かテスト書いて〜。

本日のツッコミ(全3件) [ツッコミを入れる]

moriq [validates_length_ofのテストはこんな感じでどうでしょう。 とりあえず:minimumだけ。 htt..]

moriq [Content-Typeのほうもテストを作ってみました。 http://www.moriq.com/ruby/arc..]

むとう [ありがとうございます!! ちょっと今日は時間が取れないので明日にでも。]