Undefining a Method

Problem

You want to remove an already defined method from a class or module.

Solution

From within a class or module, you can use Module#remove_method to remove a method's implementation, forcing Ruby to delegate to the superclass or a module included by a class.

In the code below, I subclass Array and override the << and [] methods to add some randomness. Then I decide that overriding [] wasn't such a good idea, so I undefine that method and get the inherited Array behavior back. The override of << stays in place.

	class RandomizingArray < Array
	 def <<(e)
	 insert(rand(size), e)
	 end

	 def [](i)
	 super(rand(size))
	 end
	end

	a = RandomizingArray.new
	a << 1 << 2 << 3 << 4 << 5 << 6 # => [6, 3, 4, 5, 2, 1]

	# That was fun; now let's get some of those entries back.
	a[0] # => 1
	a[0] # => 2
	a[0] # => 5
	#No, seriously, a[0].
	a[0] # => 4
	#It's a madhouse! A madhouse!
	a[0] # => 3
	#That does it!

	class RandomizingArray
	 remove_method('[]')
	end

	a[0] # => 6
	a[0] # => 6
	a[0] # => 6

	# But the overridden << operator still works randomly:
	a << 7 # => [6, 3, 4, 7, 5, 2, 1]

 

Discussion

Usually you'll override a method by redefining it to implement your own desired behavior. However, sometimes a class will override an inherited method to do something you don't like, and you just want the "old" implementation back.

You can only use remove_method to remove a method from a class or module that explicitly defines it. You'll get an error if you try to remove a method from a class that merely inherits that method. To make a subclass stop responding to an inherited method, you should undefine the method with undef_method.

Using undef_method on a class prevents the appropriate method signals from reaching objects of that class, but it has no effect on the parent class.

	class RandomizingArray
	 remove_method(:length)
	end
	# NameError: method 'length' not defined in RandomizingArray

	class RandomizingArray
	 undef_method(:length)
	end

	RandomizingArray.new.length
	# NoMethodError: undefined method 'length' for []:RandomizingArray
	Array.new.length # => 0

As you can see, it's generally safer to use undef_method on the class you actually want to change than to use remove_method on its parent or a module it includes.

You can use remove_method to remove singleton methods once you're done with them. Since remove_method is private, using it to remove a singleton method requires some unorthodox syntax:

	my_array = Array.new
	def my_array.random_dump(number)
	 number.times { self << rand(100) }
	end

	my_array.random_dump(3)
	my_array.random_dump(2)
	my_array # => [6, 45, 12, 49, 66]

	# That's enough of that.
	class << my_array
	 remove_method(:random_dump)
	end
	my_array.random_dump(4)
	# NoMethodError: undefined method 'random_dump' for [6, 45, 12, 49, 66]:Array

When you define a singleton method on an object, Ruby silently defines an anonymous subclass used only for that one object. In the example above, my_array is actually an anonymous subclass of Array that implements a method random_dump. Since the subclass has no name (my_array is a variable name, not a class name), there's no way of using the class syntax. We must "append" onto the definition of the my_array object.

Class methods are just a special case of singleton methods, so you can also use remove_method to remove class methods. Ruby also provides a couple of related methods for removing things besides methods. Module#remove_constant undefines a constant so that it can be redefined with a different value, as seen in Recipe 8.17. Object#remove_instance_variable removes an instance variable from a single instance of a class:

	class OneTimeContainer
	 def initialize(value)
	 @use_just_once_then_destroy = value
	 end

	 def get_value
	 remove_instance_variable(:@use_just_once_then_destroy)
	 end
	end

	object_1 = OneTimeContainer.new(6)
	object_1.get_value
	# => 6
	object_1.get_value
	# NameError: instance variable @use_just_once_then_destroy not defined

	object_2 = OneTimeContainer.new('ephemeron')
	object_2.get_value
	# => "ephemeron"

You can't remove a particular instance variable from all instances by modifying the class because the class is its own object, one which probably never defined that instance variable in the first place:

	class MyClass
	 remove_instance_variable(:@use_just_once_then_destroy)
	end
	# NameError: instance variable @use_just_once_then_destroy not defined

You should definitely not use these methods to remove methods or constants in system classes or modules: that might make arbitrary parts of the Ruby standard library crash or act unreliably. As with all metaprogramming, it's easy to abuse the power to remove and undefine methods at will.

See Also

  • Recipe 8.17, "Declaring Constants"
  • Recipe 10.5, "Fixing Bugs in Someone Else's Class"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net