January 17, 2006

Removing methods from a module

How do we remove a method from one of Ruby's built-in modules (Kernel, etc)? It should be easy and obvious, right? The following snippet should be enough, right?


#remove system from module Kernel
Kernel.send :remove_method, :system


Well, not quite. While that prevents you from calling system() in the context of the current object, you can still invoke it like this:


Kernel::system('date')


What's going on?

It turns out that they way Kernel (and other built-in modules) are defined is analogous to doing the following:


module Kernel
  def system (...)
    ...
  end

  module_function :system
end


Thus, system() is a method that will be added to instances of classes that include the given module (in our case, instances of any object, since Kernel is included by Object), but it is also a method in Kernel's singleton class (bear with me for a moment), or a module_method as the PickAxe book calls them. In Java parlance, they are simply static methods.

Looking at Ruby's source code, we can see that all of Kernel's methods are defined by way of the rb_define_module_function function, whose definition is:


void
rb_define_module_function(module, name, func, argc)
  VALUE module;
  const char *name;
  VALUE (*func)();
  int argc;
{
  rb_define_private_method(module, name, func, argc);
  rb_define_singleton_method(module, name, func, argc);
}


So, what does it mean that "system" is a method of Kernel's singleton class? Let's recap what happens when we create a class and add a method to the singleton class of one of it's instances.


class A
end

V1 = A.new    # creates an instance of A and assign it to constant V1
V2 = A.new    # ... and V2

# now we add method x() to V1's singleton class
class << V1
   def x
     puts "hello"
   end
end

V1.x    # will print "hello"
V2.x    # will fail ... V2 does not have a method x()


Clear?

Ok, now, remember that all Ruby modules are instances of a class called Module? Say we replace A by Module, V1 by Kernel and x() by system(). What we get is the following:


Kernel = Module.new

class << Kernel
   def system
     ...
   end
end


And that's essentially what's going on behind the scenes.

Going back to the question that started this post, how do we remove a method from one of the built-in modules, anyway? The answer is easy, if not as obvious as we might have expected:


module Kernel
   remove_method :system
end

class << Kernel
   remove_method :system
end


Or, more concisely,


Kernel.send :remove_method, :system
class << Kernel; self; end.send :remove_method, :system

Labels:

2 Comments:

Blogger Chuck Vose said...

Totally informative. You have a lovely style of explaining; thank you.

July 03, 2006 2:16 PM  
Blogger Unknown said...

I don’t know If I said it already, but this so good stuff keep up the good work. I read a lot of blogs on a daily basis and for the most part Belajar seo blogspot just wanted to make a quick comment to say I’m glad I found your blog. Thanks.

Best regards
Seo Motivation Oes tsetnoc faq

November 25, 2009 8:43 AM  

Post a Comment

<< Home