How do I hide a method from a superclass? - ruby

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)

Related

Creating a method that functions as both a class and instance method

Let's say I had a class and I wanted to be able to call the same method on the class itself and an instance of that class:
class Foo
def self.bar
puts 'hey this worked'
end
end
This lets me do the following:
Foo.bar #=> hey this worked
But I also want to be able to do:
Foo.new.bar #=> NoMethodError: undefined method `bar' for #<Foo:0x007fca00945120>
So now I modify my class to have an instance method for bar:
class Foo
def bar
puts 'hey this worked'
end
end
And now I can call bar on both the class and an instance of the class:
Foo.bar #=> hey this worked
Foo.new.bar #=> hey this worked
Now my class Foo is 'wet':
class Foo
def self.bar
puts 'hey this worked'
end
def bar
puts 'hey this worked'
end
end
Is there a way to avoid this redundancy?
Have one method call the other. Otherwise, no, there is no way to avoid this "redundancy", because there is no redundancy. There are two separate methods that happen to have the same name.
class Foo
def self.bar
puts 'hey this worked'
end
def bar
Foo.bar
end
end

Strange SimpleDelegator behavior

There is my code using SimpleDelegator
require 'delegate'
class Foo < SimpleDelegator
def meth
p 'new_meth'
end
end
class Bar
def meth
p 'old_meth'
end
def bar_meth
meth
end
end
bar = Bar.new
foo = Foo.new(bar)
foo.meth #=> "new_meth"
foo.bar_meth #=> "old_meth"
Why the last line gives "old_meth"???!!! Thanks!
Delegator saying:
This library provides three different ways to delegate method calls to an object. The easiest to use is SimpleDelegator. Pass an object to the constructor and all methods supported by the object will be delegated. This object can be changed later.
Okay,so now look at the output,which is to the right of the symbol # =>.
require 'delegate'
class Foo < SimpleDelegator
def meth
p 'new_meth'
end
end
class Bar
def meth
p 'old_meth'
end
def bar_meth
self.method(:meth)
end
end
bar = Bar.new # => #<Bar:0x8b31728>
foo = Foo.new(bar)
foo.__getobj__ # => #<Bar:0x8b31728>
foo.bar_meth # => #<Method: Bar#meth>
foo.method(:meth) # => #<Method: Foo#meth>
So when I used the line foo.method(:meth), then output(#<Method: Foo#meth>) confirms that whenever you will call foo.meth,then meth method of the Foo class will be called.But the line foo.bar_meth outputs(#<Method: Bar#meth>) simply saying that inside the method bar_meth,if you call meth method then, Bar#meth will be invoked.
SimpleDelegator saying that:
A concrete implementation of Delegator, this class provides the means to delegate all supported method calls to the object passed into the constructor and even to change the object being delegated to at a later time with #setobj.
Yes,in your case foo object has been set to bar object,by using #__setobj__. The output of the line foo.__getobj__ is showing that.

`respond_to?` vs. `respond_to_missing?`

What is the point of defining respond_to_missing? as opposed to defining respond_to?? What goes wrong if you redefine respond_to? for some class?
Without respond_to_missing? defined, trying to get the method via method will fail:
class Foo
def method_missing name, *args
p args
end
def respond_to? name, include_private = false
true
end
end
f = Foo.new
f.bar #=> []
f.respond_to? :bar #=> true
f.method :bar # NameError: undefined method `bar' for class `Foo'
class Foo
def respond_to? *args; super; end # “Reverting” previous redefinition
def respond_to_missing? *args
true
end
end
f.method :bar #=> #<Method: Foo#bar>
Marc-André (a Ruby core committer) has a good blog post on respond_to_missing?.
It's a good practice to create respond_to_missing? if you are overriding method_missing. That way, the class will tell you the method you are calling exists, even though it's not explicitly declared.
respond_to? should probably not be overriden :)

How can one get the list of undefined methods in a Ruby class?

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]]

Remove/undef a class method

You can dynamically define a class method for a class like so:
class Foo
end
bar = %q{def bar() "bar!" end}
Foo.instance_eval(bar)
But how do you do the opposite: remove/undefine a class method? I suspect Module's remove_method and undef_method methods might be able to be used for this purpose, but all of the examples I've seen after Googling for hours have been for removing/undefining instance methods, not class methods. Or perhaps there's a syntax you can pass to instance_eval to do this as well.
Thanks in advance.
class Foo
def self.bar
puts "bar"
end
end
Foo.bar # => bar
class <<Foo
undef_method :bar
end
# or
class Foo
singleton_class.undef_method :bar
end
Foo.bar # => undefined method `bar' for Foo:Class (NoMethodError)
When you define a class method like Foo.bar, Ruby puts it Foo's singleton class. Ruby can't put it in Foo, because then it would be an instance method. Ruby creates Foo's singleton class, sets the superclass of the singleton class to Foo's superclass, and then sets Foo's superclass to the singleton class:
Foo -------------> Foo(singleton class) -------------> Object
super def bar super
There are a few ways to access the singleton class:
class <<Foo,
Foo.singleton_class,
class Foo; class << self which is commonly use to define class methods.
Note that we used undef_method, we could have used remove_method. The former prevents any call to the method, and the latter only removes the current method, having a fallback to the super method if existing. See Module#undef_method for more information.
This also works for me (not sure if there are differences between undef and remove_method):
class Foo
end
Foo.instance_eval do
def color
"green"
end
end
Foo.color # => "green"
Foo.instance_eval { undef :color }
Foo.color # => NoMethodError: undefined method `color' for Foo:Class
You can remove a method in two easy ways. The drastic
Module#undef_method( )
removes all methods, including the inherited ones. The kinder
Module#remove_method( )
removes the method from the receiver, but it
leaves inherited methods alone.
See below 2 simple example -
Example 1 using undef_method
class A
def x
puts "x from A class"
end
end
class B < A
def x
puts "x from B Class"
end
undef_method :x
end
obj = B.new
obj.x
result -
main.rb:15:in
': undefined methodx' for # (NoMethodError)
Example 2 using remove_method
class A
def x
puts "x from A class"
end
end
class B < A
def x
puts "x from B Class"
end
remove_method :x
end
obj = B.new
obj.x
Result -
$ruby main.rb
x from A class
I guess I can't comment on Adrian's answer because I don't have enough cred, but his answer helped me.
What I found: undef seems to completely remove the method from existence, while remove_method removes it from that class, but it will still be defined on superclasses or other modules that have been extened on this class, etc.
If you would like to remove method with name what calculate dinamically, you should use eigenclasses like:
class Foo
def self.bar
puts "bar"
end
end
name_of_method_to_remove = :bar
eigenclass = class << Foo; self; end
eigenclass.class_eval do
remove_method name_of_method_to_remove
end
this way is better than others answers, becouse here i used class_eval with block. As you now block see current namespace, so you could use your variables to remove methods dinamically
Object.send(:remove_const, :Foo)

Resources