rubyのundefについて

gold対策。(基礎力確認問題12)
 
インスタンスメソッドfooをundefしているので、エラーになります。

module Mod
  def foo
    puts 'Mod'
  end
end

class Cls1
  def foo
    puts 'Cls1'
  end
end

class Cls2 < Cls1
  include Mod
  undef foo
end
Cls2.foo # =>

# ~> -:17:in `<main>': undefined method `foo' for Cls2:Class (NoMethodError)

これは通常の動きなのですが、クラスメソッドをundefする場合はどう書くのだろう?
と思ったのでやってみます。
 

クラスメソッドをundefする

ダメなやりかた

クラスやselfを指定する方法はNGです。

class C
  def self.foo; puts 'foo'; end
  undef C.foo
end
# ~> -:3: syntax error, unexpected '.', expecting keyword_end
# ~>   undef C.foo
# ~>
class C
  def self.foo; puts 'foo'; end
  undef self.foo
end
# ~> -:3: syntax error, unexpected '.', expecting keyword_end
# ~>   undef self.foo
# ~>

 
もちろんfooだけだと、インスタンスメソッドを対象にするのでエラーとなります。

class C
  def self.foo; puts 'foo'; end
  undef foo
end
# ~> -:3:in `<class:C>': undefined method `foo' for class `C' (NameError)
# ~>    from -:1:in `<main>'

 

これだとOK

特異クラスでやります。

class C
  def self.foo; puts 'foo'; end

  class << self
    undef foo
  end
end

class_evalなら。(これはあんまり関係ないか)

class C
  def self.foo; puts 'foo'; end
end

C.class_eval do
  class << self
    undef foo;
  end
end

 
 

undefとundef_methodについて

2つとも同様の機能ですが、引数に違いがあります。 undef文(式?)は、メソッド or Symbol が指定可能です。 undef_methodはメソッドなのでSymbolとStringです。  
・undef文

class A
  def foo; puts 'foo'; end
  undef foo
end

class B
  def foo; puts 'foo'; end
  undef :foo
end

ちなみに文字列はダメです。

class A
  def foo; puts 'foo'; end
  undef "foo"
end

・undef_method

class A
  def foo; puts 'foo'; end
  undef_method :foo
end
class A
  def foo; puts 'foo'; end
  undef_method 'foo'
end

 
 

そもそもundefなんか使うのか?

ってことですが、メタプログラミングRuby(2章)で紹介されているように
ブランクスレートという使い方があるかと思います。
ブランクスレートは、メソッドをすべて削除した状態のことです。  
method_missing()を安全に使う方法として、
レシーバのメソッドは削除するが、継承したメソッドをそのままにする
ということをやろうとすると、下記のようにundef_method()使って書けます。

class Computer
  instance_methods.each do |m|
    undef_method m unless m.to_s =~ /^__|method_missing|respond_to?/
  end
end

正規表現のところは、__id__, __send__、method_missing(), respond_to?()を残すためです。