From: SASADA Koichi Date: 2009-02-13T05:52:57+09:00 Subject: [ruby-dev:37982] Re: proposal: Module#method_adding  ささだです. SASADA Koichi wrote:: >  今,asakusa.rb で酒を飲んでたんですが,Module#method_adding とか,そう > いうメソッドというのはどうでしょうか. メソッドが定義される直前に呼ばれ > るメソッドです. > > やりたいこと: >  例えば,foo というメソッドのオーバーライドをしたときに,old_foo のよう > な古いメソッドの alias を自動的に作るにはどうすれば,という話になりまし > た.Module#method_added を使うとどうだろう,と思ったのですが,これはすで > にメソッドを上書きした後でよばれるようです. > >  ということで,メソッドを定義する直前にフックするためのメソッドとして > Module#method_adding というのを新設するのはどうだろう,という案がでまし > た.すでに,そういうことをする方法があればいいんですが,なければ,一つ検 > 討ご検討いただけないでしょうか. >  なんとなく実装してみました.ちなみに,method_added の rdoc がなかった ので,singleton_method_added をコピーして適当に書きました.  気づいた点は2点. * ...ed 系のフックメソッドに,...ing 系を付け加えたくなってくる 結構きりがない.hook 関数全般を set_trace_func みたいにまとめる? * singleton_*ed があるメソッドは *ed の rdoc がない. Index: object.c =================================================================== --- object.c (リビジョン 22272) +++ object.c (作業コピー) @@ -523,6 +523,91 @@ rb_obj_tap(VALUE obj) */ /* + * Document-method: method_adding + * + * call-seq: + * method_adding(symbol) + * + * Invoked as a callback whenever a method (except singleton method) + * is adding to the class. + * This method is called before method adding process. + * + * module Chatty + * def Chatty.method_adding(id) + * puts "adding: #{id}, respond_to?: #{method_defined? id}" + * end + * def Chatty.method_added(id) + * puts "added: #{id}, respond_to?: #{method_defined? id}" + * end + * def self.one() end + * def two() end + * def Chatty.three() end + * end + * + * produces: + * + * adding: two, respond_to?: false + * added: two, respond_to?: true + * + */ + +/* + * Document-method: method_added + * + * call-seq: + * method_added(symbol) + * + * Invoked as a callback whenever a method (except singleton method) + * is added to the class. + * + * module Chatty + * def Chatty.singleton_method_added(id) + * puts "Adding #{id}" + * end + * def self.one() end + * def two() end + * def Chatty.three() end + * end + * + * produces: + * + * Adding 2 + * + */ + +/* + * Document-method: singleton_method_adding + * + * call-seq: + * singleton_method_adding(symbol) + * + * Invoked as a callback whenever a singleton method is adding to the + * receiver. This method is called before method added process. + * + * module Chatty + * def Chatty.singleton_method_adding(id) + * puts "adding: #{id}, respond_to?: #{self.respond_to? id}" + * end + * def Chatty.singleton_method_added(id) + * puts "added: #{id}, respond_to?: #{self.respond_to? id}" + * end + * def self.one() end + * def two() end + * def Chatty.three() end + * end + * + * produces: + * + * adding: singleton_method_added, respond_to?: false + * added: singleton_method_added, respond_to?: true + * adding: one, respond_to?: false + * added: one, respond_to?: true + * adding: three, respond_to?: false + * added: three, respond_to?: true + * + */ + +/* * Document-method: singleton_method_added * * call-seq: @@ -533,7 +618,7 @@ rb_obj_tap(VALUE obj) * * module Chatty * def Chatty.singleton_method_added(id) - * puts "Adding #{id.id2name}" + * puts "Adding #{id}" * end * def self.one() end * def two() end @@ -2488,6 +2573,7 @@ Init_Object(void) rb_define_method(rb_cBasicObject, "!=", rb_obj_not_equal, 1); rb_define_private_method(rb_cBasicObject, "singleton_method_added", rb_obj_dummy, 1); + rb_define_private_method(rb_cBasicObject, "singleton_method_adding", rb_obj_dummy, 1); rb_define_private_method(rb_cBasicObject, "singleton_method_removed", rb_obj_dummy, 1); rb_define_private_method(rb_cBasicObject, "singleton_method_undefined", rb_obj_dummy, 1); @@ -2497,6 +2583,7 @@ Init_Object(void) rb_define_private_method(rb_cModule, "included", rb_obj_dummy, 1); rb_define_private_method(rb_cModule, "extended", rb_obj_dummy, 1); rb_define_private_method(rb_cModule, "method_added", rb_obj_dummy, 1); + rb_define_private_method(rb_cModule, "method_adding", rb_obj_dummy, 1); rb_define_private_method(rb_cModule, "method_removed", rb_obj_dummy, 1); rb_define_private_method(rb_cModule, "method_undefined", rb_obj_dummy, 1); Index: vm_method.c =================================================================== --- vm_method.c (リビジョン 22272) +++ vm_method.c (作業コピー) @@ -10,7 +10,7 @@ static void rb_vm_check_redefinition_opt static ID object_id; static ID removed, singleton_removed, undefined, singleton_undefined; -static ID added, singleton_added; +static ID added, singleton_added, adding, singleton_adding; struct cache_entry { /* method hash table. */ ID mid; /* method's id */ @@ -125,6 +125,17 @@ rb_add_method(VALUE klass, ID mid, NODE if (OBJ_FROZEN(klass)) { rb_error_frozen("class/module"); } + + if (node && mid != ID_ALLOCATOR && ruby_running) { + if (FL_TEST(klass, FL_SINGLETON)) { + rb_funcall(rb_iv_get(klass, "__attached__"), singleton_adding, 1, + ID2SYM(mid)); + } + else { + rb_funcall(klass, adding, 1, ID2SYM(mid)); + } + } + rb_clear_cache_by_id(mid); /* @@ -749,6 +760,13 @@ rb_alias(VALUE klass, ID name, ID def) singleton = rb_iv_get(klass, "__attached__"); } + if (singleton) { + rb_funcall(singleton, singleton_adding, 1, ID2SYM(name)); + } + else { + rb_funcall(klass, adding, 1, ID2SYM(name)); + } + orig_fbody->nd_cnt++; if (st_lookup(RCLASS_M_TBL(klass), name, &data)) { @@ -1132,7 +1150,9 @@ Init_eval_method(void) object_id = rb_intern("object_id"); added = rb_intern("method_added"); + adding = rb_intern("method_adding"); singleton_added = rb_intern("singleton_method_added"); + singleton_adding = rb_intern("singleton_method_adding"); removed = rb_intern("method_removed"); singleton_removed = rb_intern("singleton_method_removed"); undefined = rb_intern("method_undefined"); -- // SASADA Koichi at atdot dot net