[Ruby] evil-ruby

http://evil.rubyforge.org/

コンセプトはわからないけど、become とか superclass= とか
Class#as_module
こんなのないかなぁ。。。と思っていたものがここにあった。
やってることは、おそらくCでの構造体レベルでのデータを変更したりしてる(はず
だけど、dl/structを使ってrubyのみで書かれてるのがすごい。

./test/tc_*.rb 試してみた所、テストが全部通らなかった。

  • rprofile付きで実行した時と結果が代わるので、不安定なのかな。

ソースのコメント内にも FIXME memory leak と書いてある。(汗

      • -

自分でも数日前に丁度似たような目的のモジュールを書いた事があったので、
その時のコードを載せてみる。

#include "ruby.h"

static VALUE
mop_set_superclass(self, klass)
  VALUE self;
  VALUE klass;
{
  if (TYPE(klass) != T_CLASS) rb_raise(rb_eTypeError, "");
  if (RCLASS(self)->super != rb_cObject) rb_clear_cache();
  RCLASS(self)->super = klass;
  return self;
}

void
Init_mop(void)
{
  rb_mMop = rb_define_module("MetaObjectProtocol");
  rb_define_method(rb_mMop, "superclass=", mop_set_superclass, 1);
}
 ruby -rmkmf -e "create_makefile('mop')" && make
#!/usr/bin/env ruby
require 'mop.so'

Foo = Class.new{define_method("say_hello"){puts "hello, foo"}}
Bar = Class.new{define_method("say_hello"){puts "hello, bar"}}
c = Class.new(Foo).extend(MetaObjectProtocol)
o = c.new
o.say_hello # hello, foo
c.superclass = Bar # 動的にスーパークラスを変更
o.say_hello # hello, bar

superclassへの代入だけでは、
superclassを何回か変更した時にメソッド名が衝突すると、
cache内の以前のsuperclassのメソッドを呼び出してしまう事があった。
(evilのforumにもpostしてみた)

let binding

 def let(*values); yield *values; end
 def let(*values, &block)
   block.call(*values) if block_given?
 end


example:

 let (metainfo['info']) { |info|
   info['name'] = name
   info['pieces] = pieces.join
   info['piece length'] = 2**18
 }

If write this with lambda or proc.

 lambda {|info|
  # : same
 }.call(metainfo['info'])

Since ruby's function can take a block argument, and have closure,
It works like let binding. but its in limited context,
there was an exception which i dont expect

 def let(*values); yield *values; end

 n = num = 0
 s = str = "Hello"

 p n, num
 p s, str

 let (num) { |n| p n; n = n } # left n is global, right n is local...
 let (str) { |s| p s; s = "world" }

 p n, num
 p s, str

In the block, p n shows value of num,
but n = 1000 changes global variable 'n'. its not local variable !?
seem this is known problem at ruby.

I'm thinking how to define 'with' statement.

Aversa [Ruby][BitTorrent]

http://aversa.rubyforge.org/

 Aversa is a little utility for creating and viewing BitTorrent metainfo files.

メタ情報ファイル(.torrentファイル)の表示と生成のみだけど、
トラッカーも作りそうな雰囲気。Net::BitTorrentモジュールとか作ってたので
プロジェクトにマージお願いしてみた。

元々、興味があったのは peer-wire-protcol の部分のみだったので、
実験用にダウンロード出来る最小限のコードだけ書いて
アプリケーションまでは作らずに放置していたのだけど、
この機会にもう少し手入れしてみる事にする。


ダウンローダーとかトラッカーはみんな作ってると思うので、
焦点をずらして、BitTorrentの応用技術に注目してみようと思う。

rubyforge.orgがトラッカーを提供し始めたので、検索機能と連係できるかも知れない。
データベースを共有すればクローラーは必要ないかな。
ちなみにプロジェクトのadminはrubyforge.orgのサポートもやってる(?らしい)


  • MetaInfoのRDF版 (textで保存/編集できる、XSLで表示しやすいなどの利点)

メタ情報ファイルのXML化はWikiにもアイデアが挙がってた。
実装はまだ見たことない。
バイナリが問題なりそうだけど、適切にエンコードすれば no problem
例えば pieces は、

 
  :
  ...
 

トラッカーがRDFとかでデータ返してくれるとうれしい。
BEncodeよりXMLの方が普及しているし、大抵の開発環境でXMLが使える。
メタ情報ファイルの文字エンコードの問題も解決する。
欠点は、当然データが冗長になる事。pieceseがbase64だと 4/3 倍 + タグや
ヘッダの分増加。ただし、これは HTTP/gzip による圧縮が期待できる。

出来るかどうかわからないけどアイデアだけ書き綴っておく。
他にも wikiBitTorrent Spec2 拡張のアイデアが多数あり。

[JavaScript] (mozilla)

 var code = uneval(this["function_name"])
 code = code.replace("...", "...") // codeを編集
 this["function_name"] = eval("(" + code + ")")

uneval/evalを使って、macroの様な事が出来る。
これの応用でJavaScriptで継続を実装なんてデモがあった。

LISPなら、S式=構文木=codeだから、リスト操作で済むのだけど
javascript/evalの場合は、
コードを再度パースするか、正規表現&置換でコードを編集しないといけない。
tokenくらい簡単に得られたらなぁ、、

関数オブジェクトは上記の方法で得られるので、
AOPのPointCutみたいなのは匿名関数と組み合わせで出来る。
(先日のProthonでの例みたいに)

 this["foo"] = function () { return afterFunc(this["foo"](arguments)) }

[PrUnit]

ソースを全部張り付けるには多き過ぎたので、
"test"で始まるメソッドを呼び出す処理の部分のみ。

def run():
    for attrs in self.attrs_.items():
        name = attrs[0]
        func = attrs[1]
        if name[:4] == "test":
            self.setUp()
            func{self}() # or self.attrs_[func]()
            self.tearDown()
    self.report()
  • Pythonでは dir(), get_attr, set_attr, has_attrを使うが、Prothonではメソッド/フィールドを保持する attrs_ フィールドを使う。Pythonクラスの__dict__フィールドみたいなもの。
  • Prothonでの慣習では、under-barで終る名前は private。

About prototype base ...

Prothonの場合、
ClassとObjectだけでなく、ClassとModuleの区別もないようなきがする。
例えば、aspect.prに通常の関数として定義するだけで、
import 先からは aspect(class)のメソッドして扱う事も出来る。
Pythonではimportする名前はmodule名になる。