From: Hidetoshi NAGAI Date: 2007-11-06T12:41:02+09:00 Subject: [ruby-dev:32171] autoload_delete 永井@知能.九工大です. 現状の 1.9 では,autoload 登録しているファイルを 明示的に require しようとすると例外が発生します. # 1.8 でこれをやると,対象ファイルを2回読んでしまうらしいことに # 初めて気が付きました.(^_^; この仕様の場合,「利便性のために autoload 登録はしているものの, 直接 require する人もいる」というケースで困ってしまいます. 要は,対象ファイル中の定数定義に対して autoload が起動してしまうのが 問題ですから,対象ファイルの頭で autoload 定義を殺してやれば 利用者の利便性を阻害することなく回避できるはずです. しかし,現状では明示的に autoload 定義を取り除くメソッドが 存在しないように見受けられます. そこで Kernel#autoload_delete, Module#autoload_delete を 追加するというのはいかがでしょうか? これを用いれば,例えば --------------------------------------------- autoload_delete(:ZZZ) class ZZZ $XXX ||= 0 $XXX += 1 p $XXX # ... defs ... end ----------------------------------------------------- に対して, ----------------------------------------------------- autoload(:ZZZ, 'zzz') ZZZ.new ----------------------------------------------------- でも ----------------------------------------------------- require 'zzz' ZZZ.new ----------------------------------------------------- でも ----------------------------------------------------- autoload(:ZZZ, 'zzz') require 'zzz' ZZZ.new ----------------------------------------------------- でもエラーにならずに動いてくれます. # ついでに,1.8 のように $XXX が 2 になったりすることもなく.(^_^) パッチを以下に添付します. なお,autoload(name, nil) で削除というのも考えましたが, 登録抹消時に以前に登録されていたファイルを返した方が便利と 思われましたので,戻り値の性格の違いから別メソッドとしています. ------------------------------------------------------------------------ Index: eval_load.c =================================================================== --- eval_load.c (revision 13825) +++ eval_load.c (working copy) @@ -512,6 +512,27 @@ } /* + * call-seq: + * mod.autoload_delete(name) => filename or nil + * + * Unregisters the autoload definition about _name_. + * Returns the unregisterd _filename_ (when registered by autoload) or + * nil (when no registered). + * + * module A + * end + * A.autoload(:B, "b") + * A.autoload_delete(:B) # returns "b" + * A.autoload_delete(:C) # returns nil + */ + +static VALUE +rb_mod_autoload_delete(VALUE mod, VALUE sym) +{ + return rb_autoload_delete(mod, rb_to_id(sym)); +} + +/* * MISSING: documentation */ @@ -543,6 +564,31 @@ } /* + * call-seq: + * autoload_delete(module) => filename or nil + * + * Unregisters the autoload definition about _module_. + * the first time that _module_ (which may be a String or + * a symbol) is accessed. + * Returns the unregisterd _filename_ (when registered by autoload) or + * nil (when no registered). + * + * autoload(:MyModule, "/foo/modules/my_module.rb") + * autoload_delete(:MyModule) # returns "/foo/modules/my_module.rb" + * autoload_delete(:MyNoModule) # returns nil + */ + +static VALUE +rb_f_autoload_delete(VALUE mod, VALUE sym) +{ + VALUE klass = ruby_cbase(); + if (NIL_P(klass)) { + rb_raise(rb_eTypeError, "Can not set autoload on singleton class"); + } + return rb_mod_autoload_delete(klass, sym); +} + +/* * MISSING: documentation */ @@ -572,8 +618,10 @@ rb_define_global_function("load", rb_f_load, -1); rb_define_global_function("require", rb_f_require, 1); rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2); + rb_define_method(rb_cModule, "autoload_delete", rb_mod_autoload_delete, 1); rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1); rb_define_global_function("autoload", rb_f_autoload, 2); + rb_define_global_function("autoload_delete", rb_f_autoload_delete, 1); rb_define_global_function("autoload?", rb_f_autoload_p, 1); ruby_dln_librefs = rb_ary_new(); Index: include/ruby/intern.h =================================================================== --- include/ruby/intern.h (revision 13825) +++ include/ruby/intern.h (working copy) @@ -570,6 +570,7 @@ VALUE rb_class_name(VALUE); void rb_autoload(VALUE, ID, const char*); VALUE rb_autoload_load(VALUE, ID); +VALUE rb_autoload_delete(VALUE, ID); VALUE rb_autoload_p(VALUE, ID); void rb_gc_mark_global_tbl(void); VALUE rb_f_trace_var(int, VALUE*); Index: variable.c =================================================================== --- variable.c (revision 13825) +++ variable.c (working copy) @@ -1398,6 +1398,18 @@ } VALUE +rb_autoload_delete(VALUE klass, ID id) +{ + VALUE file; + NODE *load = autoload_delete(klass, id); + + if (!load || !(file = load->nd_lit) || rb_provided(RSTRING_PTR(file))) { + return Qnil; + } + return file; +} + +VALUE rb_autoload_p(VALUE mod, ID id) { struct st_table *tbl = RCLASS_IV_TBL(mod); ------------------------------------------------------------------------ -- 永井 秀利 (九工大 知能情報) nagai@ai.kyutech.ac.jp