ruby undefine aliases when undefining method - ruby

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

Related

Undefine class method and back to define it later

I need to disable a class method for some time, then enable it again. How could I do that? I know that I can remove a method:
class Foo
def Foo.bar
puts "bar"
end
end
Foo.bar # => bar
class <<Foo
remove_method :bar
end
Foo.bar # => undefined method `bar' for Foo:Class (NoMethodError)
Now I need Foo.bar again. How could I do that? I tried to save the method in a proc
m = Proc.new { Foo.bar }
and then define it again:
class Foo
define_method(:bar, &m)
end
but I get
NameError: undefined local variable or method 'm' for...
So I flattened the scope
P = Class.new(Foo) do
define_method(:bar, &m)
end
but I get undefined method if I run it
P.bar
Foo.bar
Is it possible to save a method, undefine it, and then define it back?
Instead of keeping a method body in a proc, you should keep methods as methods. Constantly keep it defined under some different name, and switch Foo.bar between alias of it or not.
class Foo
def Foo.temporal_bar
puts "bar"
end
end
Foo.singleton_class.class_eval{alias bar temporal_bar}
Foo.bar # => bar
Foo.singleton_class.class_eval{remove_method bar}
Foo.bar # => Undefined local variable or method `bar' for #<Class:Foo>
Foo.singleton_class.class_eval{alias bar temporal_bar}
Foo.bar # => bar
You can try out instance_eval:
(class << Foo; self; end).instance_eval do
define_method(:bar, &m)
end

How do I hide a method from a superclass?

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)

undef singleton method from ruby object

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)

confusion with `Class instance methods` call

I was trying if I can call Class instance methods by the instances of class or not. Accordingly tried the below:
class Foo
def show; p "hi" ; end
def self.display ; p "hello" ; end
end
#=> nil
Foo.display
#"hello"
#=> "hello"
Foo.new.show
#"hi"
#=> "hi"
Foo.show
#NoMethodError: undefined method `show' for Foo:Class
#from (irb):7
#from C:/Ruby200/bin/irb:12:in `<main>'
But in the below call I expect the same error as NoMethodError: undefined method `display'. But why is it not the case?
Foo.new.display
#<Foo:0x538020> #=> nil
foo = Foo.new
#=> #<Foo:0x22bc438>
foo.display
#<Foo:0x22bc438> #=> nil
There is an existing method display on all objects.
class Bar
end
Bar.new.methods.grep(/disp/) # => [:display]
Bar.methods.grep(/disp/) # => [:display]
Your code just overwrites it for instances of Foo. Choose another name (display1, for example) and you'll see expected error.

dynamically defining instance methods but not class methods

Ruby 1.9.3: I am trying to implement a method which takes as argument a class, a symbol and a proc and defines (eventually overwriting) a new instance method for that class, but not a class method so that this test should pass:
require 'test/unit'
include Test::Unit::Assertions
class String
def my_method
'my_method'
end
end
def define_instance_method(klass, method, &block)
# klass.send :define_method, method, &block
# ...
end
define_instance_method(Object, :my_method) { 'define_instance_method my_method' }
define_instance_method(String, :my_method) { 'define_instance_method my_method' }
assert Object.new.my_method == 'define_instance_method my_method'
assert_raise(NoMethodError) { Object.my_method }
assert String.new.my_method == 'define_instance_method my_method'
assert_raise(NoMethodError) { String.my_method }
I did many tryings (define_method, define_singleton_method, class_eval, instance_eval, instance_exec, module_eval...) but with no success; do you have any ideas?
#ProGNOMmers, recall that objects in Ruby are classes and classes are objects.
So everything in Ruby are objects, even classes :)
And when you define a method inside Object it will also be available to Class as well as to any class that inherits from Object.
That's mean to all classes.
Some proof:
define_instance_method(String, :my_method) { 'define_instance_method my_method' }
p String.new.my_method
p String.my_method
# => "define_instance_method my_method"
# => stackoverflow.rb:11:in `<main>': undefined method `my_method' for String:Class (NoMethodError)
See, your method not yet defined inside Object, thus not available inside String.
Now let's do it:
define_instance_method(Object, :my_method) { 'define_instance_method my_method' }
p String.my_method
# => "define_instance_method my_method"
p Array.my_method
# => "define_instance_method my_method"
p NilClass.my_method
# => "define_instance_method my_method"
So just comment define_instance_method(Object... in your tests
and String specs will pass just well.
Here are some crash tests
class_eval should work for you. Let me know why it isn't working for you
class A
end
A.class_eval do
define_method(:foo) {puts 'hello'}
end
A.new.foo #hello
A.foo #NoMethodError: undefined method `foo' for A:Class

Resources