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
How to Sort a String's Characters Alphabetically
How to Get the Target of a Symlink
Case-Insensitive Array#Include
How to Set the Default String Encoding on Ruby 1.9
Ruby/Sinatra - Serving Up CSS, JavaScript, or Image Files
Sidekiq Not Deallocating Memory After Workers Have Finished
Migrating from Rails 2 to Rails 3
Ruby To_JSON Issue with Error "Illegal/Malformed Utf-8"
Why Doesn't Rails' "Errors.Full_Messages" Replace Attribute and Message Variables
How to Use Dot Syntax for Ruby Hash
How to Make a Ruby String Safe for a Filesystem
Which Algorithm Does Ruby's Sort Method Use
How to Convert PDF Files to Images Using Rmagick and Ruby
How to Get a Date from a Week Number