読者です 読者をやめる 読者になる 読者になる

意図しないメソッドの上書きを防ぐテクニック

Rubyには意図しないメソッドの上書きを防止する方法があります。このテクニックは例えば、既にあるクラスに独自拡張を加えるときなどに有効です。既に定義されているクラスをAとし(Aが例えばStringの場合では既に定義されています。Aのままの場合は、既にクラスが定義されているわけではありませんが、Rubyスクリプトとしては間違いではありません)、独自拡張したAのメソッドをa_methodとします。

#!/usr/local/bin/ruby
#ファイル名:extend_class.rb
class Module
  def check_method(method)
    if method_defined?(method)
      $stderr.puts "Warning:#{self}##{method} already exists"
    else
      yield
    end
  end
end
class A
  check_method("a_method") do
    def a_method; end
  end
end

このとき、このファイルを間接的にrequireするようなスクリプトはAにa_methodが追加されていることを知りません。もしかしたらこちらのスクリプトでもa_methodが定義されている可能性があります。このようなとき、既に定義されていると、警告を出すと誤動作の原因をすぐに特定することができます。上のファイルextend_class.rbに試しにクラスBを次のようにたして

class B < A
  check_method("a_method") do
    def a_method; end
  end
end

実行してみると、"Warning:B#a_method already exists"と表示される。
このテクニックはRake(0.8.7)で使われている。

######################################################################
# Rake extensions to Module.
#
class Module
  # Check for an existing method in the current class before extending.  IF
  # the method already exists, then a warning is printed and the extension is
  # not added.  Otherwise the block is yielded and any definitions in the
  # block will take effect.
  #
  # Usage:
  #
  #   class String
  #     rake_extension("xyz") do
  #       def xyz
  #         ...
  #       end
  #     end
  #   end
  #
  def rake_extension(method)
    if method_defined?(method)
      $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists"
    else
      yield
    end
  end
end # module Module