Remove/Undef a Class Method

Remove/undef a class method


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.

How to remove a class method?

Your difficulty is not down to the method being a class method but because the method is defined in a module. Firstly you need to be clear on the difference between remove_method and undef_method.

remove_method removes a method from the class/module that defined it (ie the one that contains the corresponding def or on which define_method was called). If you try and call that method, ruby will still try to search superclasses and included modules. Here remove_method isn't working for you because the receiver is B's singleton class but the method wasn't defined there (it was defined on the anonymous module), hence the error about the method not being defined on the class.

undef_method prevents a class from responding to a method, irrespective of where that method was defined. That's why extending a new module after calling undef_method doesn't work: you've told ruby not to search the ancestors for that method.

What will work however is calling remove_method on the module that you've extended your class with. This will stop that inplementation of settings being used bur won't interfere if the class is extended with another module defines the method.

Removing/undefining a class method that's included by another module

Use undef instead of remove_method:

require 'httparty'

class Foo
include HTTParty
class << self
undef :get
end
end

Foo.get #=> NoMethodError: undefined method `get' for Foo:Class

Cancels the method definition. Undef can not appear in the method
body. By using undef and alias, the interface of the class can be
modified independently from the superclass, but notice it may be broke
programs by the internal method call to self.
http://web.njit.edu/all_topics/Prog_Lang_Docs/html/ruby/syntax.html#undef

When to use undef_method, and when to use remove_method?

From the fine manual:

undef_method(symbol) → self

Prevents the current class from responding to calls to the named method. Contrast this with remove_method, which deletes the method from the particular class; Ruby will still search superclasses and mixed-in modules for a possible receiver.

So a remove_method like this:

class CC < C
remove_method :m
end

is essentially the opposite of this:

class CC < C
def m
end
end

Where def m adds the method m to the class, remove_method :m removes m. But, if the super class has an m method, then that will still be used.

undef_method, OTOH, is more like this:

class CC < C
def m
raise 'No, you cannot do that.'
end
end

So undef_method doesn't actually remove the method, it replaces the method with a special internal flag that causes Ruby to complain if you try to call that method.

Sounds like you're trying to replace an existing method and replace is semantically the same as a remove followed by an add so remove_method is probably more appropriate. However, if you want to be paranoid and make sure the replacement method is in place, then undef_method would be useful; or, if for some reason you need to remove the method in one place and add it in another, undef_method would at least tell you that you only did half the job whereas remove_method would either leave you with the super class's implementation (and possible strange bugs) or a rather confusing NoMethodError.

How to undefine class in Ruby?


class Foo; end
# => nil
Object.constants.include?(:Foo)
# => true
Object.send(:remove_const, :Foo)
# => Foo
Object.constants.include?(:Foo)
# => false
Foo
# NameError: uninitialized constant Foo

EDIT Just noticed your edit, removing the constant is probably not the best way to achieve what you're looking for. Why not just move one of the Contact classes into a separate namespace.

EDIT2 You could also rename your class temporarily like this:

class Foo
def bar
'here'
end
end

TemporaryFoo = Foo
Object.send(:remove_const, :Foo)
# do some stuff
Foo = TemporaryFoo
Foo.new.bar #=> "here"

Again, the trouble with this is that you'll still have the newer Contact class so you'll have to remove that again. I would really recommend name-spacing your classes instead. This will also help you avoid any loading issues

Remove a method only from an instance

Yes, it is possible:

f1.instance_eval('undef :a_method')


Related Topics



Leave a reply



Submit