2

While playing a bit with Ruby, I wrote the following code:

class A
end

A.singleton_class.instance_eval do
    undef_method :new
end

# or
# class << B
#   undef_method :new
# end

A.new

> NoMethodError: undefined method `new' for A:Class
>         from (irb):8
>         from /home/mmsequeira/.rvm/rubies/ruby-1.9.3-p327/bin/irb:16:in `<main>'

This is cool. But how can I know which methods have been undefined in a given class?

MMSequeira
  • 61
  • 6
  • You can't. Why do you need to know? –  Nov 25 '12 at 16:31
  • Just exploring Ruby. Ruby's reflection is very good, so it seems odd that one cannot get this information. – MMSequeira Nov 25 '12 at 16:33
  • This answer may provide some additional insight for you. http://stackoverflow.com/questions/6468268/get-list-of-a-class-methods – allareri Nov 25 '12 at 16:41
  • 1
    @allareri I don't think so. – sawa Nov 25 '12 at 16:56
  • Thanks! I guess I could use `Class.instance_methods - A.methods` for class methods, `T.superclass.instance_methods - T.instance_methods` for instance (non-singleton) methods, and `T.instance_methods - t.methods` for singleton methods. But still, that would leave out methods defined and then undefined in the class itself. – MMSequeira Nov 25 '12 at 17:02
  • 3
    FWIW Ruby does keep track of undefined methods internally (`VM_METHOD_TYPE_UNDEF` -- that's how it ensures a future call won't land on e.g. a base class implementation, see [differences between `undef_method` and `remove_method`](http://www.nach-vorne.de/2008/2/28/undef_method-remove_method/index.html)), but looking at the Ruby source I fail to see a way of retrieving a list of these methods short of writing native code. – vladr Nov 25 '12 at 17:32
  • @vladr Right, it stores it internally but there's no way to get at them without writing a C extension that reaches into the core. – Andrew Marshall Nov 25 '12 at 17:34
  • Seems like an oversight of Ruby's reflection API, @AndrewMarshall. – MMSequeira Nov 25 '12 at 21:28
  • 1
    @MMSequeira oversight may not be the reason; more likely, the thought may have been that since you can't do anything with it (such as un-`undef` it) then why need you know that it exists? In other words, if it no longer quacks, do you really need to know that it used to be a duck? – vladr Nov 25 '12 at 23:40
  • If I store the method, I can un-undef it: `class A; def foo; "A#foo"; end; end; foo = A.instance_method :foo; class A; undef foo; end; A.instance_eval { define_method :foo, foo }`. – MMSequeira Nov 26 '12 at 00:35

2 Answers2

3

You can't by default. Undefining a method removes it from existence. You could, however, record them as they're removed. This can be done with method hooks to capture everything and avoid ugly alias method chaining:

class Module
  def method_undefined name
    (@undefined_methods ||= []) << name
  end

  def singleton_method_undefined name
    (@undefined_methods ||= []) << name
  end

  def undefined_methods
    @undefined_methods || []
  end
end

This will capture methods undefined via undef_method or undef:

class C
  def foo; end
  def bar; end

  undef foo
  undef_method :bar
end

C.undefined_methods  #=> [:foo, :bar]
C.singleton_class.instance_eval { undef new }
C.singleton_class.undefined_methods  #=> [:new]

Of course, you must include the hook methods in Module before anything can be captured.

Community
  • 1
  • 1
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
1

Maybe you need to redefine Module#undef_method.

class Module
  alias original_undef_method :undef_method
  @@undef_methods = {}
  def undef_method *methods
    methods.each{|method| @@undef_methods[[self, method]] ||= true}
    original_undef_method(*methods)
  end
  def self.undef_methods; @@undef_methods.keys end
end

Then, you get:

class A
end
A.singleton_class.instance_eval do
    undef_method :new
end
Module.undef_methods
# => [[#<Class:A>, :new]]
sawa
  • 165,429
  • 45
  • 277
  • 381