Module#freezeの謎
これは読んでも役に立たない記事です。
RubyのModuleクラスは、Object#freezeを使わずにわざわざ独自にModule#freezeを実装しています。なんで?
Rubyのソースコードを見ると、Module#freezeは関数rb_class_nameを呼んでからObject#freeze(関数rb_obj_freeze)を呼び出しています(ruby-1.9.1-p376のobject.c 1176行目)。
static VALUE rb_mod_freeze(VALUE mod) { rb_class_name(mod); return rb_obj_freeze(mod); }
関数rb_class_nameを遡って関数rb_class_pathを見ると、次のような記述があります(ruby-1.9.1-p376のvariable.c 186行目)。また、関数rb_class_nameは、Module#to_sでも使われています。
VALUE rb_class_path(VALUE klass) { VALUE path = classname(klass); if (!NIL_P(path)) return path; if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass), tmp_classpath, &path)) { return path; } else { (中略) rb_ivar_set(klass, tmp_classpath, path); return path; }
無名のモジュールやクラスでto_sが呼び出されると、文字列pathを作ってインスタンス変数tmp_classpathにとりあえず保存し、次にto_sが呼び出されたら、インスタンス変数の文字列を返す、という実装になっています。
Module#freezeがObject#freezeをそのまま呼び出す実装だと、凍結されたモジュールのto_sを呼び出したときにインスタンス変数への書き込みが発生し、エラーになってしまいます。なので、Module#freezeを新しく定義し、to_sの文字列を作っておいてからObject#freezeを呼ぶ、となっています。
次のコードでModule#freezeを削除すると、エラーが起きるのを確認できます。
Module.send :remove_method, :freeze c = Class.new c.freeze c.to_s rescue p $!
#<RuntimeError: can't modify frozen object>