これまで Kodougu では、prototype.js + script.aculo.us を使ってきたのですが、dojo.gfx(SVG と VML が使えるグラフィックスライブラリ)を使ってみたかったので、dojo に切り替えてみました。

dojo.gfx のコードは以下のような感じになります。(SVG か VML を知っているとなじみやすいです。)


  dojo.gfx example. 
  
  
  

  


  

  



This is a dojo.gfx example.


上記のコードのスクリーンショットは以下の通りです。
1177037914_20070420.png

また、dojo(0.4.2)を含む JavaScript に file:// でアクセスすると、IE7 で以下のようなエラーが発生しました。FireFox ではこのようなことは無かったので、IE がらみの問題かもしれませんが dojo.require に関連するバグかもしれません。(詳細は追っていません。)IE7 でも、Apache 上に配置して http:// でアクセスしたところ、問題なく動作しました。
symbol 'dojo.gfx' is not defined after loading '__package__.js'

概ね、dojo.gfx には満足していますが、SVG で言うところの text 要素に対応していないなど、完成度はまだまだ低いようです。

Posted by あかさた
最近、JavaScript のテストを行う方法を探していました。xUnit 系の JsUnitMochiKit に付属してくるテストフレームワークが有名どころのようです。とりあえずテスト用のメソッド名になじみのある JsUnit を使ってみることにします。

■ 目的
alert デバッグから開放される!

■ この記事に書かれていること
・ JsUnit の基本的な情報
・ 複数のテストファイルを同時に実行する方法
・ testRunner.html で毎回テストファイル名を指定しないでテストを実行する方法

■ JsUnit の概要
まず、testRunner.html というテストランナーが存在します。html ファイルの script タグ内にテストコードを書いて、テストランナーにテストファイル(html)を指定すると、テストコードを実行するという仕組みになっています。

まずは動かしてみてください。JsUnit を DL すると、testRunner.html というファイルが含まれているので、このファイルをブラウザで開いてください。テストファイル名の入力を促されるので、tests フォルダ内の適当な html ファイル(テストファイル)をフルパス指定して、Run ボタンを押してください。見慣れたグリーンバーが表示されるはずです。

テストの書き方は、テストファイルを見ればわかります。イメージとしては以下のようなコードになります。

<script language="JavaScript" type="text/javascript" 
  src="./framework/app/jsUnitCore.js">
</script>
<script language="JavaScript" type="text/javascript">
function testAssert() {
    assert("true should be true", true);
    assert(true);
}
</script>

■ テストのディレクトリ構造
とりあえず、私は以下のような構造にしてみました。
 + e:\tests
   + framework <- ここに testRunner.html など JsUnit の全てのファイル/フォルダを入れる
   - tests.html <- テストコードの一覧を持つ
   - xxxTest.html <- テストコード

■ suite を使って複数のテストファイルを実行する
複数のテストファイルを同時に実行するには、tests.html に以下のように書いて、tests.html を testRunner.html 実行してください。
<script language="JavaScript" type="text/javascript" 
    src="./framework/app/jsUnitCore.js">
</script>
<script language="JavaScript" type="text/javascript">
function suite(){
  var newsuite = new top.jsUnitTestSuite();
  newsuite.addTestPage("../xxxTest.html");
  return newsuite;
}
</script>

テストファイル名を指定する際、tests.html からではなく、testRunner.html からの相対パスを指定していることに注意してください。

■ URL にテストファイル名を含めることで、毎回の入力を回避する
毎回 testRunner.html にテストファイル名を入力するのが面倒なので、URL のパラメータ「testpage」を指定すると、ファイル名を自動的に入力してくれます。以下のような URL を作成してお気に入りに入れておくと良いでしょう。

URL の例:file:///E:/tests/framework/testRunner.html?testpage=E:/tests/tests.html

■ 参考
Javascript/JsUnit - Bobchin's Wiki
http://bobchin.ddo.jp/wiki/index.php?Javascript%2FJsUnit


Posted by あかさた
Ruby では i++ はサポートしていないようです。これまで気づいていませんでした。イテレータが準備されるようになると for 文を書かなくなるので、これまで i++ のような表記をしなかったということでしょうか。

Ruby での書き方:
i += 1

ruby-list におけるまつもと氏の見解:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/126

この文法は驚き最小の法則からは外れる・・・気がするのですが、Delphi 出身の私にとってはむしろ自然に感じたりします。(笑)

Delphi での書き方:
Inc(i);

なぜか、Delphi では上記のように書くと高速でした。Delphi はインライン展開をサポートしていなかったと思いますが、こういう関数は特別扱いされていて、アセンブラか何かで実装されていて、その処理をインライン展開していたのかもしれません。
# 今となってはどうでも良いことですが。

Posted by あかさた
act_as_authenticated に自動生成された UserTest の test_should_reset_password が失敗するという現象が発生しています。具体的には以下のコードで失敗します。
# 前からこんな現象あったかなー???

def test_should_reset_password
  users(:quentin).update_attributes(:password => 'new password', 
    :password_confirmation => 'new password')
  assert_equal users(:quentin), User.authenticate('quentin', 'new password')
end

エラーの発生条件:RadRails 0.7.1 上で Run Unit Tests を実行する、rake test:units を実行する

ただし、上記テストメソッド単体を実行すると、テストは成功することから、他のテストメソッドの実行結果に影響されて失敗していることがわかります。DB の内容を見ると、users(:quentin) の login(ユーザ名)は 'quentin' ではなく 'quentin2' になっています。このことから、test_should_reset_password は、test_should_not_rehash_password に依存しているようです。

Ruby(と Rails)の UnitTest フレームワークの性質をよく知らないのですが、テストメソッドを実行するたびに DB の初期化が実行されればこういった問題は発生しないと考えられるので、DB の初期化を行うタイミング(=fixtures メソッドが呼ばれるタイミング)はテストクラスを読み込む一度きりということでしょう。

とはいえ、原因がわかっても、テストコードの修正は簡単ではありません。他のテストの実行の有無で結果が変わってしまうテストを変わらないコードにするというのはなかなか難しいことです。とりあえず以下のように考えてみました。

このテストのやりたいこと:レコード更新時にパスワードを変更する(:password_confirmation があるときは、パスワードを更新することを確認したい。※)

※ そういう意味では、更新時に :password のみを変更して :password_confirmation を変更しない or 間違っているテストが無いことは少し疑問です。もっとも、生成時のテストはあるからそれで問題はありませんが。

修正案1:レコード更新時に login も更新する
def test_should_reset_password
  users(:quentin).update_attributes(:login => 'quentin', 
    :password => 'new password', :password_confirmation => 'new password')
  assert_equal users(:quentin), User.authenticate('quentin', 'new password')
end

メリット:修正は簡単
デメリット:テストコードの意図がやや不明瞭になる

修正案2:更新したユーザのインスタンスから名前を取得して、認証を行う
def test_should_reset_password
  users(:quentin).update_attributes(:password => 'new password', 
    :password_confirmation => 'new password')
  assert_equal users(:quentin), User.authenticate(users(:quentin).login, 'new password')
end

メリット:テストの意図はそのまま
デメリット:元と比べてコードがわかりにくい?(というか、なんか気持ち悪い・・・)

とりあえず、修正案2で行きます。


Posted by あかさた
Kodougu で必要だったので、Rails アプリ上で Ruby の eval を使用したときの安全性を調査していました。ご存知のとおり、eval 関数は、文字列で渡された Ruby ソースコードを実行するものです。しかし、外部からスクリプトを渡して実行しようとすると、セキュリティの観点から危険な場合があります。以下のような例(外部から渡された script が DB を操作している)です。

script = <<-EOS
  # ActiveRecord を操作する
  @metaelement = Metaelement.new
  @metaelement.name = "execute_eval"
  @metaelement.save
EOS

Thread.start {
  # $SAFE = 4 <- このコメントアウトを外すと SecurityError 例外発生
  eval(script)
}.join

デフォルトでは上記のコードは動作します。安全ではない eval です。しかし、$SAFE = 4 にして上記のコードを実行すると、SecurityError 例外が発生します。Ruby のセキュリティモデル Level 4 には、「汚染されていないオブジェクトの状態の変更」を禁止するルールがあるので、これに引っかかったようです。Level 4 では以下のようなコードでも SecurityError 例外が発生します。

script = <<-EOS
  @metaelements = Metaelement.find(:all)
EOS

Thread.start {
  $SAFE = 4
  eval(script)
}.join

では、Rails でも Level 4 で eval すれば安全・・・というと、ちょっと自信がありません。たとえば以下のようなコードは動作してしまいます。

script = <<-EOS
  Metaelement.connection # DB のコネクションを取得。
EOS

Thread.start {
  $SAFE = 4
  eval(script)
}.join

上記のようなコードの動作を許可すると、DB のユーザ、パスワードとかを取れてしまいます。見られたくない情報です。eval の用途によっては非常に危険です。危険な例としては、ページ上で eval で評価した結果を表示する場合などが挙げられます。Rails の全メソッドに rb_secure(1) とかを仕込めばいいのでしょうかね。(^^; (ありとあらゆる面から論外でしょうけど。)

Ruby on Rails でサンドボックスというのはさすがに無理ですかね~。。。


Posted by あかさた
平鍋さんのブログを読んでいたら、以下のような記述がありました。

JUDE 今期('06/4-'07/3)の販売目標 - An Agile Way より

チェンジビジョンの決算を迎える訳ですが、JUDE/Professional の有償版販売目標である、6千本を、もう少しで達成します。


保守更新を含まないでですよね? 私もこの手のツールのビジネスに関わっていたことがあるので、この本数が結構すごい(※)ことは承知しています。それでも、無償版が数十万本も出ていたことを考えると、思ったほど多くは無いなという感じを受けました。売り上げ的には保守更新もあるでしょうから、問題はなさそうですけど。

※ JUDE は無償版も結構使えてしまいますから、なおさら凄い。

Kodougu も近いといえば近い(遠いといえば遠い)ツールなので、こういうビジネス的な話題には思わず注目してしまいますね。(^^;

Posted by あかさた
私は Ruby の module のテストは以下のように行っています。

[code: def test_hoge

@dummy_object = Object.new
@dummy_object.extend(HogeModule)
assert ...hogehoge...
end]

他にも以下のような方法も考えられます。クラスを生成してからモジュールを拡張する方法です。

[code: def test_hoge

@new_class = Class.new
@dummy_object = @new_class.allocate
@dummy_object.extend(HogeModule)
assert ...hogehoge...
end]

コードとしては以下がわかりやすいかも。明示的にテスト用のクラスを作成する場合です。

[code: class Dummy

include HogeModule
end

def test_hoge
@dummy_object = Dummy.new
assert ...hogehoge...
end]

Ruby の module は Java や C# には無い概念なのでどういうテストをしたらいいのか悩みますね。

Posted by あかさた
Rails を動作させるプラットフォームとしては、scgi よりも Mongrel に力が入っているという話なので、Kodougu と同じくこのブログも Apache2.2 + Mongrel に変更しました。ついでに、Rails 1.2 に対応したのですが、ちゃんと動いているでしょうか・・・?

このブログについてはこちらを参照してください。ソースコードのチェックアウトなどもできます。テストもロクに書いていないひどいコードですが。

■ 2007/3/16 22:44 追記
従来はロードバランスしていなかったので、scgi サーバでは一つのコネクションしか処理できませんでした。アクセス数が少ないので、通常のブラウジングであればそれでも問題ないのですが、他のブログにトラックバック送信を行おうとすると問題になります。

送信先のブログが言及リンクチェックをする場合ですが、チェックのためにこちらのサーバに接続すると、ブログ投稿者がコネクションを占有してしまっているので、送信先のブログはコネクションが空くのを待つようになり、こちらのサーバは送信先のトラックバック完了待ちになって、デッドロック状態になります。投稿者の接続がタイムアウトすると、送信先の言及リンクチェックが完了し、トラックバックが完了します。

こんなばかばかしい現象も、ロードバランスをしたおかげで発生しなくなりました。

Posted by あかさた
最近私が実践している「くそエディタ」を立ち上げる方法について。

「くそエディタ」については以下を参照してください。

射撃しつつ前進 - The Joel on Software Translation Project
http://local.joelonsoftware.com/mediawiki/index.php/%E5%B0%84%E6%92%83%E3%81%97%E3%81%A4%E3%81%A4%E5%89%8D%E9%80%B2

このやり方は Windows にのみ対応しています。でも別に他の OS でも実践できるでしょう。[スタート]-[すべてのプログラム]-[スタートアップ]にエディタもしくは IDE のショートカットを追加します。すると、PC を起動すると、勝手にエディタが立ち上がってくれます。間違っても、スタートアップでメーラやブラウザを立ち上げないこと。

わーい、これでたくさんプログラムが打てるぞ!

Posted by あかさた
Ruby プログラムを .NET Framework 2.0 上で動作する DLL/EXE に変換する Ruby.NET Compiler ソフトウェアがあります。気になったのでちょっと調べてみました。

本記事は、Gardens Point Ruby.NET Compiler Beta Release(February 2007)に対応しています。

■ 知りたいこと
・ 既存の Ruby ライブラリが使えるか
・ 既存の .NET ライブラリが使えるか
・ 既存のアプリケーションに組み込めるか
・ ライセンスは使いやすいか
・ 安定度はどれくらいか
・ 開発は活発か

■ 調査結果
・ 既存の Ruby ライブラリが使えるか
実装していないようです。実装予定があるかどうかも不明です。

・ 既存の .NET ライブラリが使えるか
次のリリースで対応する(したい)ようです。

・ 既存のアプリに組み込めるか
ちゃんとコードは追っていませんが、高い頻度で動的に書き換えるのはパフォーマンス的にネックになりそう(※)ですが、それほどでなければ大丈夫そうです。

※ コンパイラであるためです。ただ、IronPython もコンパイル可能でわりと動的に書き換えてもそれほどパフォーマンスでネックにはならないので、このツールもこなれてくれば大丈夫ではないかなと楽観しています。IronPython を組み込む際は、ランタイムのロード時間のほうが問題になりました(IronPython というよりは .NET 系全般の問題)。ゲームとかリアルタイム性が必要なアプリの場合は、ランタイムを呼び出すタイミングをコントロールする必要があります。
(IronPython では、WinXP/P4 2.8GHz/1GB の PC で秒間 50 フレームで HelloWorld をパースさせたら CPU 使用率が 100% になった <- あたりまえ^^;)

コンパイラなので、既存のアプリに組み込むことは想定に入っていないのか、プログラミングインタフェースは、まだ整理されていないように見えました。IronPython のように 1 行で組み込むというわけにはいかないという印象を受けました。

・ ライセンスは使いやすいか
BSD ライセンスに近い印象を受けました。このページの Licensing という箇所を参照してください。以下の内容が書かれています。というわけで結構使いやすいのではないかと思っています。

続きを読む

Posted by あかさた