зеркало из https://github.com/github/ruby.git
Support optional inherit argument for Module#method_defined?
Module has many introspection methods for methods and constants that either return an array or return true or false for whether the method or constant is defined. Most of these methods support an optional argument that controls whether to consider inheritance. Currently, the following Module methods support such a argument: * const_defined? * constants * instance_methods * private_instance_methods * protected_instance_methods * public_instance_methods and the following methods do not: * method_defined? * private_method_defined? * protected_method_defined? * public_method_defined? This patch supports such an argument for the *method_defined? methods. While you can currently work around the lack of support via: mod.instance_methods(false).include?(:method_name) This patch allows the simpler and more efficient: mod.method_defined?(:method_name, false) One case where you want to exclude inheritance when checking for a method definition is when you want to replace a method that may already exist. To avoid a verbose warning, you want to remove the method only if it is already defined: remove_method(:foo) if method_defined?(:foo, false) define_method(:foo){} You can't call remove_method without checking for the method definition, as that can raise a NameError, and you don't want to include inheritance because remove_method will still raise a NameError if the method is defined by an ancestor and not by the module itself. [ruby-core:88140] [Feature #14944] From: Jeremy Evans <code@jeremyevans.net> git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64337 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
50e41f4a4f
Коммит
bccb24a866
|
@ -468,17 +468,42 @@ class TestModule < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_method_defined?
|
||||
assert !User.method_defined?(:wombat)
|
||||
assert User.method_defined?(:mixin)
|
||||
assert User.method_defined?(:user)
|
||||
assert User.method_defined?(:user2)
|
||||
assert !User.method_defined?(:user3)
|
||||
[User, Class.new{include User}, Class.new{prepend User}].each do |klass|
|
||||
[[], [true]].each do |args|
|
||||
assert !klass.method_defined?(:wombat, *args)
|
||||
assert klass.method_defined?(:mixin, *args)
|
||||
assert klass.method_defined?(:user, *args)
|
||||
assert klass.method_defined?(:user2, *args)
|
||||
assert !klass.method_defined?(:user3, *args)
|
||||
|
||||
assert !User.method_defined?("wombat")
|
||||
assert User.method_defined?("mixin")
|
||||
assert User.method_defined?("user")
|
||||
assert User.method_defined?("user2")
|
||||
assert !User.method_defined?("user3")
|
||||
assert !klass.method_defined?("wombat", *args)
|
||||
assert klass.method_defined?("mixin", *args)
|
||||
assert klass.method_defined?("user", *args)
|
||||
assert klass.method_defined?("user2", *args)
|
||||
assert !klass.method_defined?("user3", *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_method_defined_without_include_super
|
||||
assert User.method_defined?(:user, false)
|
||||
assert !User.method_defined?(:mixin, false)
|
||||
assert Mixin.method_defined?(:mixin, false)
|
||||
|
||||
User.const_set(:FOO, c = Class.new)
|
||||
|
||||
c.prepend(User)
|
||||
assert !c.method_defined?(:user, false)
|
||||
c.define_method(:user){}
|
||||
assert c.method_defined?(:user, false)
|
||||
|
||||
assert !c.method_defined?(:mixin, false)
|
||||
c.define_method(:mixin){}
|
||||
assert c.method_defined?(:mixin, false)
|
||||
|
||||
assert !c.method_defined?(:userx, false)
|
||||
c.define_method(:userx){}
|
||||
assert c.method_defined?(:userx, false)
|
||||
end
|
||||
|
||||
def module_exec_aux
|
||||
|
@ -974,8 +999,8 @@ class TestModule < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_method_defined
|
||||
c = Class.new
|
||||
c.class_eval do
|
||||
cl = Class.new
|
||||
def_methods = proc do
|
||||
def foo; end
|
||||
def bar; end
|
||||
def baz; end
|
||||
|
@ -983,33 +1008,47 @@ class TestModule < Test::Unit::TestCase
|
|||
protected :bar
|
||||
private :baz
|
||||
end
|
||||
cl.class_eval(&def_methods)
|
||||
sc = Class.new(cl)
|
||||
mod = Module.new(&def_methods)
|
||||
only_prepend = Class.new{prepend(mod)}
|
||||
empty_prepend = cl.clone
|
||||
empty_prepend.prepend(Module.new)
|
||||
overlap_prepend = cl.clone
|
||||
overlap_prepend.prepend(mod)
|
||||
|
||||
assert_equal(true, c.public_method_defined?(:foo))
|
||||
assert_equal(false, c.public_method_defined?(:bar))
|
||||
assert_equal(false, c.public_method_defined?(:baz))
|
||||
[[], [true], [false]].each do |args|
|
||||
[cl, sc, only_prepend, empty_prepend, overlap_prepend].each do |c|
|
||||
always_false = [sc, only_prepend].include?(c) && args == [false]
|
||||
|
||||
# Test if string arguments are converted to symbols
|
||||
assert_equal(true, c.public_method_defined?("foo"))
|
||||
assert_equal(false, c.public_method_defined?("bar"))
|
||||
assert_equal(false, c.public_method_defined?("baz"))
|
||||
assert_equal(always_false ? false : true, c.public_method_defined?(:foo, *args))
|
||||
assert_equal(always_false ? false : false, c.public_method_defined?(:bar, *args))
|
||||
assert_equal(always_false ? false : false, c.public_method_defined?(:baz, *args))
|
||||
|
||||
assert_equal(false, c.protected_method_defined?(:foo))
|
||||
assert_equal(true, c.protected_method_defined?(:bar))
|
||||
assert_equal(false, c.protected_method_defined?(:baz))
|
||||
# Test if string arguments are converted to symbols
|
||||
assert_equal(always_false ? false : true, c.public_method_defined?("foo", *args))
|
||||
assert_equal(always_false ? false : false, c.public_method_defined?("bar", *args))
|
||||
assert_equal(always_false ? false : false, c.public_method_defined?("baz", *args))
|
||||
|
||||
# Test if string arguments are converted to symbols
|
||||
assert_equal(false, c.protected_method_defined?("foo"))
|
||||
assert_equal(true, c.protected_method_defined?("bar"))
|
||||
assert_equal(false, c.protected_method_defined?("baz"))
|
||||
assert_equal(always_false ? false : false, c.protected_method_defined?(:foo, *args))
|
||||
assert_equal(always_false ? false : true, c.protected_method_defined?(:bar, *args))
|
||||
assert_equal(always_false ? false : false, c.protected_method_defined?(:baz, *args))
|
||||
|
||||
assert_equal(false, c.private_method_defined?(:foo))
|
||||
assert_equal(false, c.private_method_defined?(:bar))
|
||||
assert_equal(true, c.private_method_defined?(:baz))
|
||||
# Test if string arguments are converted to symbols
|
||||
assert_equal(always_false ? false : false, c.protected_method_defined?("foo", *args))
|
||||
assert_equal(always_false ? false : true, c.protected_method_defined?("bar", *args))
|
||||
assert_equal(always_false ? false : false, c.protected_method_defined?("baz", *args))
|
||||
|
||||
# Test if string arguments are converted to symbols
|
||||
assert_equal(false, c.private_method_defined?("foo"))
|
||||
assert_equal(false, c.private_method_defined?("bar"))
|
||||
assert_equal(true, c.private_method_defined?("baz"))
|
||||
assert_equal(always_false ? false : false, c.private_method_defined?(:foo, *args))
|
||||
assert_equal(always_false ? false : false, c.private_method_defined?(:bar, *args))
|
||||
assert_equal(always_false ? false : true, c.private_method_defined?(:baz, *args))
|
||||
|
||||
# Test if string arguments are converted to symbols
|
||||
assert_equal(always_false ? false : false, c.private_method_defined?("foo", *args))
|
||||
assert_equal(always_false ? false : false, c.private_method_defined?("bar", *args))
|
||||
assert_equal(always_false ? false : true, c.private_method_defined?("baz", *args))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_top_public_private
|
||||
|
|
134
vm_method.c
134
vm_method.c
|
@ -1278,14 +1278,43 @@ rb_mod_undef_method(int argc, VALUE *argv, VALUE mod)
|
|||
return mod;
|
||||
}
|
||||
|
||||
static rb_method_visibility_t
|
||||
check_definition_visibility(VALUE mod, int argc, VALUE *argv)
|
||||
{
|
||||
const rb_method_entry_t *me;
|
||||
VALUE mid, include_super, lookup_mod = mod;
|
||||
int inc_super;
|
||||
ID id;
|
||||
|
||||
rb_scan_args(argc, argv, "11", &mid, &include_super);
|
||||
id = rb_check_id(&mid);
|
||||
if (!id) return METHOD_VISI_UNDEF;
|
||||
|
||||
if (argc == 1) {
|
||||
inc_super = 1;
|
||||
} else {
|
||||
inc_super = RTEST(include_super);
|
||||
if (!inc_super) {
|
||||
lookup_mod = RCLASS_ORIGIN(mod);
|
||||
}
|
||||
}
|
||||
|
||||
me = rb_method_entry_without_refinements(lookup_mod, id, NULL);
|
||||
if (me) {
|
||||
if (!inc_super && me->owner != mod) return METHOD_VISI_UNDEF;
|
||||
return METHOD_ENTRY_VISI(me);
|
||||
}
|
||||
return METHOD_VISI_UNDEF;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* mod.method_defined?(symbol) -> true or false
|
||||
* mod.method_defined?(string) -> true or false
|
||||
* mod.method_defined?(symbol, inherit=true) -> true or false
|
||||
* mod.method_defined?(string, inherit=true) -> true or false
|
||||
*
|
||||
* Returns +true+ if the named method is defined by
|
||||
* _mod_ (or its included modules and, if _mod_ is a class,
|
||||
* its ancestors). Public and protected methods are matched.
|
||||
* _mod_. If _inherit_ is set, the lookup will also search _mod_'s
|
||||
* ancestors. Public and protected methods are matched.
|
||||
* String arguments are converted to symbols.
|
||||
*
|
||||
* module A
|
||||
|
@ -1306,6 +1335,8 @@ rb_mod_undef_method(int argc, VALUE *argv, VALUE mod)
|
|||
* A.method_defined? :method1 #=> true
|
||||
* C.method_defined? "method1" #=> true
|
||||
* C.method_defined? "method2" #=> true
|
||||
* C.method_defined? "method2", true #=> true
|
||||
* C.method_defined? "method2", false #=> false
|
||||
* C.method_defined? "method3" #=> true
|
||||
* C.method_defined? "protected_method1" #=> true
|
||||
* C.method_defined? "method4" #=> false
|
||||
|
@ -1313,37 +1344,26 @@ rb_mod_undef_method(int argc, VALUE *argv, VALUE mod)
|
|||
*/
|
||||
|
||||
static VALUE
|
||||
rb_mod_method_defined(VALUE mod, VALUE mid)
|
||||
rb_mod_method_defined(int argc, VALUE *argv, VALUE mod)
|
||||
{
|
||||
ID id = rb_check_id(&mid);
|
||||
if (!id || !rb_method_boundp(mod, id, 1)) {
|
||||
return Qfalse;
|
||||
}
|
||||
return Qtrue;
|
||||
|
||||
rb_method_visibility_t visi = check_definition_visibility(mod, argc, argv);
|
||||
return (visi == METHOD_VISI_PUBLIC || visi == METHOD_VISI_PROTECTED) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
check_definition(VALUE mod, VALUE mid, rb_method_visibility_t visi)
|
||||
check_definition(VALUE mod, int argc, VALUE *argv, rb_method_visibility_t visi)
|
||||
{
|
||||
const rb_method_entry_t *me;
|
||||
ID id = rb_check_id(&mid);
|
||||
if (!id) return Qfalse;
|
||||
me = rb_method_entry_without_refinements(mod, id, NULL);
|
||||
if (me) {
|
||||
if (METHOD_ENTRY_VISI(me) == visi) return Qtrue;
|
||||
}
|
||||
return Qfalse;
|
||||
return (check_definition_visibility(mod, argc, argv) == visi) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* mod.public_method_defined?(symbol) -> true or false
|
||||
* mod.public_method_defined?(string) -> true or false
|
||||
* mod.public_method_defined?(symbol, inherit=true) -> true or false
|
||||
* mod.public_method_defined?(string, inherit=true) -> true or false
|
||||
*
|
||||
* Returns +true+ if the named public method is defined by
|
||||
* _mod_ (or its included modules and, if _mod_ is a class,
|
||||
* its ancestors).
|
||||
* _mod_. If _inherit_ is set, the lookup will also search _mod_'s
|
||||
* ancestors.
|
||||
* String arguments are converted to symbols.
|
||||
*
|
||||
* module A
|
||||
|
@ -1358,26 +1378,28 @@ check_definition(VALUE mod, VALUE mid, rb_method_visibility_t visi)
|
|||
* def method3() end
|
||||
* end
|
||||
*
|
||||
* A.method_defined? :method1 #=> true
|
||||
* C.public_method_defined? "method1" #=> true
|
||||
* C.public_method_defined? "method2" #=> false
|
||||
* C.method_defined? "method2" #=> true
|
||||
* A.method_defined? :method1 #=> true
|
||||
* C.public_method_defined? "method1" #=> true
|
||||
* C.public_method_defined? "method1", true #=> true
|
||||
* C.public_method_defined? "method1", false #=> true
|
||||
* C.public_method_defined? "method2" #=> false
|
||||
* C.method_defined? "method2" #=> true
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_mod_public_method_defined(VALUE mod, VALUE mid)
|
||||
rb_mod_public_method_defined(int argc, VALUE *argv, VALUE mod)
|
||||
{
|
||||
return check_definition(mod, mid, METHOD_VISI_PUBLIC);
|
||||
return check_definition(mod, argc, argv, METHOD_VISI_PUBLIC);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* mod.private_method_defined?(symbol) -> true or false
|
||||
* mod.private_method_defined?(string) -> true or false
|
||||
* mod.private_method_defined?(symbol, inherit=true) -> true or false
|
||||
* mod.private_method_defined?(string, inherit=true) -> true or false
|
||||
*
|
||||
* Returns +true+ if the named private method is defined by
|
||||
* _ mod_ (or its included modules and, if _mod_ is a class,
|
||||
* its ancestors).
|
||||
* _mod_. If _inherit_ is set, the lookup will also search _mod_'s
|
||||
* ancestors.
|
||||
* String arguments are converted to symbols.
|
||||
*
|
||||
* module A
|
||||
|
@ -1392,26 +1414,28 @@ rb_mod_public_method_defined(VALUE mod, VALUE mid)
|
|||
* def method3() end
|
||||
* end
|
||||
*
|
||||
* A.method_defined? :method1 #=> true
|
||||
* C.private_method_defined? "method1" #=> false
|
||||
* C.private_method_defined? "method2" #=> true
|
||||
* C.method_defined? "method2" #=> false
|
||||
* A.method_defined? :method1 #=> true
|
||||
* C.private_method_defined? "method1" #=> false
|
||||
* C.private_method_defined? "method2" #=> true
|
||||
* C.private_method_defined? "method2", true #=> true
|
||||
* C.private_method_defined? "method2", false #=> false
|
||||
* C.method_defined? "method2" #=> false
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_mod_private_method_defined(VALUE mod, VALUE mid)
|
||||
rb_mod_private_method_defined(int argc, VALUE *argv, VALUE mod)
|
||||
{
|
||||
return check_definition(mod, mid, METHOD_VISI_PRIVATE);
|
||||
return check_definition(mod, argc, argv, METHOD_VISI_PRIVATE);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* mod.protected_method_defined?(symbol) -> true or false
|
||||
* mod.protected_method_defined?(string) -> true or false
|
||||
* mod.protected_method_defined?(symbol, inherit=true) -> true or false
|
||||
* mod.protected_method_defined?(string, inherit=true) -> true or false
|
||||
*
|
||||
* Returns +true+ if the named protected method is defined
|
||||
* by _mod_ (or its included modules and, if _mod_ is a
|
||||
* class, its ancestors).
|
||||
* _mod_. If _inherit_ is set, the lookup will also search _mod_'s
|
||||
* ancestors.
|
||||
* String arguments are converted to symbols.
|
||||
*
|
||||
* module A
|
||||
|
@ -1426,16 +1450,18 @@ rb_mod_private_method_defined(VALUE mod, VALUE mid)
|
|||
* def method3() end
|
||||
* end
|
||||
*
|
||||
* A.method_defined? :method1 #=> true
|
||||
* C.protected_method_defined? "method1" #=> false
|
||||
* C.protected_method_defined? "method2" #=> true
|
||||
* C.method_defined? "method2" #=> true
|
||||
* A.method_defined? :method1 #=> true
|
||||
* C.protected_method_defined? "method1" #=> false
|
||||
* C.protected_method_defined? "method2" #=> true
|
||||
* C.protected_method_defined? "method2", true #=> true
|
||||
* C.protected_method_defined? "method2", false #=> false
|
||||
* C.method_defined? "method2" #=> true
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_mod_protected_method_defined(VALUE mod, VALUE mid)
|
||||
rb_mod_protected_method_defined(int argc, VALUE *argv, VALUE mod)
|
||||
{
|
||||
return check_definition(mod, mid, METHOD_VISI_PROTECTED);
|
||||
return check_definition(mod, argc, argv, METHOD_VISI_PROTECTED);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -2121,10 +2147,10 @@ Init_eval_method(void)
|
|||
rb_define_private_method(rb_cModule, "private", rb_mod_private, -1);
|
||||
rb_define_private_method(rb_cModule, "module_function", rb_mod_modfunc, -1);
|
||||
|
||||
rb_define_method(rb_cModule, "method_defined?", rb_mod_method_defined, 1);
|
||||
rb_define_method(rb_cModule, "public_method_defined?", rb_mod_public_method_defined, 1);
|
||||
rb_define_method(rb_cModule, "private_method_defined?", rb_mod_private_method_defined, 1);
|
||||
rb_define_method(rb_cModule, "protected_method_defined?", rb_mod_protected_method_defined, 1);
|
||||
rb_define_method(rb_cModule, "method_defined?", rb_mod_method_defined, -1);
|
||||
rb_define_method(rb_cModule, "public_method_defined?", rb_mod_public_method_defined, -1);
|
||||
rb_define_method(rb_cModule, "private_method_defined?", rb_mod_private_method_defined, -1);
|
||||
rb_define_method(rb_cModule, "protected_method_defined?", rb_mod_protected_method_defined, -1);
|
||||
rb_define_method(rb_cModule, "public_class_method", rb_mod_public_method, -1);
|
||||
rb_define_method(rb_cModule, "private_class_method", rb_mod_private_method, -1);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче