I would like to be able to undef a singleton method on a given object.
class A
end
a = A.new
def a.foo
puts "bar"
end
# undef a.foo here
a.foo # should crash
class << a
undef foo
end
Alternatively:
a.singleton_class.send :undef_method, :foo
class A
end
a = A.new
def a.foo
puts "bar"
end
a.instance_eval { undef :foo }
a.foo # => undefined method `foo' for #<A:0x8469c60> (NoMethodError)
Related
I noticed in Ruby when you undefine a method, it does NOT undefine aliases:
class Foo
end
Foo.instance_eval do
def bar
puts 'bar'
end
end
Foo.bar # => bar
Foo.instance_eval {alias :baar :bar }
Foo.instance_eval { undef :bar }
Foo.bar # => NoMethodError: undefined method `bar' for Foo:Class
Foo.baar # => bar
What is the correct approach to undefine all aliases associated with a method when that method is deleted via undef?
You can list all methods aliased to a specific method by selecting all methods equal to it (see here), so you could define a method like this:
class Class
def undef_with_aliases(method_name)
methods.select { |m| method(m) == method(method_name) }.each { |m| instance_eval "undef #{m}" }
end
end
Foo.instance_eval { undef_with_aliases :bar }
Foo.bar # => NoMethodError: undefined method `bar' for Foo:Class
Foo.baar # => NoMethodError: undefined method `baar' for Foo:Class
Is there a way to define a method foo on module/class A so that it will be visible only from within module/class B, or its descendants? The following illustrates this situation:
A.new.foo # => undefined
class B
A.new.foo # => defined
def bar
A.new.foo # => defined
end
def self.baz
A.new.foo # => defined
end
end
class C < B
A.new.foo # => defined
def bar
A.new.foo # => defined
end
def self.baz
A.new.foo # => defined
end
end
I intuitively felt refinement was close in spirit, but it does not seem to do what I want.
This works. ^_^
class A
private
def foo
"THE FOO !!!"
end
end
class B < A
public :foo
def initialize
#foo = self.foo
end
end
puts "A.new.foo #{ A.new.foo rescue '... sorry, no.' }"
=> A.new.foo ... sorry, no.
puts "B.new.foo #{ B.new.foo rescue '... sorry, no.' }"
=> B.new.foo THE FOO !!!
If you want to use the A.new.foo within all sub classes by still using the A class name then you should use the following.
class A
private
def foo
"THE FOO !!!"
end
end
class B
class A < A
public :foo
end
attr_reader :c, :d
def c
A.new.foo
end
def d
A.new.foo
end
end
puts "A.new.foo #{ A.new.foo rescue '... sorry, no.' }"
=> A.new.foo ... sorry, no.
puts B.new.c
=> THE FOO !!!
puts B.new.d
=> THE FOO !!!
I have a method in a superclass from a third-party gem which I want to hide. I would prefer if it is impossible to call this method at all, so not just override it and leave the body empty.
I believe this may be what you're looking for:
undef_method :foo
This will prevent any calls to the method foo.
In contrast, this will not achieve the same effect:
remove_method :foo
That will remove the method from the child, but will still pass through up the inheritance chain.
Docs: undef_method and remove_method
Use the undef keyword.
class A
def foo
5
end
end
class B < A
undef foo
end
A.new.foo #=> 5
B.new.foo #=> NameError: undefined local variable or method `foo'
That is wrong OOP that you're trying to do there. I suggest you use composition instead of inheritance.
require 'forwardable'
class SomeBaseClass
def foo
puts 'foo'
end
def bar
puts 'bar'
end
def quux
puts 'quux'
end
end
class MyClass
def initialize
#base = SomeBaseClass.new
end
extend Forwardable
def_delegators :#base, :foo, :bar # but not quux
end
mc = MyClass.new
mc.foo
mc.bar
mc.quux
# >> foo
# >> bar
# ~> -:32:in `<main>': undefined method `quux' for #<MyClass:0x007febcc155210> (NoMethodError)
While playing a bit with Ruby, I wrote the following code:
class A
end
A.singleton_class.instance_eval do
undef_method :new
end
# or
# class << B
# undef_method :new
# end
A.new
> NoMethodError: undefined method `new' for A:Class
> from (irb):8
> from /home/mmsequeira/.rvm/rubies/ruby-1.9.3-p327/bin/irb:16:in `<main>'
This is cool. But how can I know which methods have been undefined in a given class?
You can't by default. Undefining a method removes it from existence. You could, however, record them as they're removed. This can be done with method hooks to capture everything and avoid ugly alias method chaining:
class Module
def method_undefined name
(#undefined_methods ||= []) << name
end
def singleton_method_undefined name
(#undefined_methods ||= []) << name
end
def undefined_methods
#undefined_methods || []
end
end
This will capture methods undefined via undef_method or undef:
class C
def foo; end
def bar; end
undef foo
undef_method :bar
end
C.undefined_methods #=> [:foo, :bar]
C.singleton_class.instance_eval { undef new }
C.singleton_class.undefined_methods #=> [:new]
Of course, you must include the hook methods in Module before anything can be captured.
Maybe you need to redefine Module#undef_method.
class Module
alias original_undef_method :undef_method
##undef_methods = {}
def undef_method *methods
methods.each{|method| ##undef_methods[[self, method]] ||= true}
original_undef_method(*methods)
end
def self.undef_methods; ##undef_methods.keys end
end
Then, you get:
class A
end
A.singleton_class.instance_eval do
undef_method :new
end
Module.undef_methods
# => [[#<Class:A>, :new]]
Here is an example:
class MyClass
end
obj = MyClass.new
obj.instance_eval do
def hello
"hello"
end
end
obj.hello
# => "hello"
obj.methods.grep "hello"
# => ["hello"]
MyClass.instance_methods.grep "hello"
# => []
MyClass's instance methods don't contain 'hello' method, so My question is where Ruby stores the method defined in instance_eval()?
Look at this:
obj = MyClass.new
def obj.hello
"hello"
end
obj.hello #=> "hello"
obj.singleton_methods #=> [:hello]
obj.methods.grep :hello #=> [:hello]
obj.instance_eval do
def hello2 ; end
end #
obj.singleton_methods #=> [:hello, :hello2]
As you can see instead of using instance_eval you can also define a method directly on an object. In both cases they end up in the object's singleton class (eigenclass), which can be accessed via obj.singleton_class in Ruby 1.9 and the class << self ; self; end idiom in Ruby 1.8.