概要
上書きできない魔法みたいな最強メソッドを定義できるぞ!
本編
さて、上書きできないメソッド、どうやって定義しようか?
まずはprepend
かなあ。匿名モジュールも活用しよう。
prependを使う
class Foo
mod = Module.new
prepend mod
mod.module_eval do
def saikyo
puts "I'm saikyo!"
end
end
end
class Foo
def saikyo
puts "You're not!"
end
end
Foo.new.saikyo # => I'm saikyo!
おお、オープンクラスでも上書きできないぞ!
でも、これ継承されたらダメよね。
class Bar < Foo
def saikyo
puts "You're not!"
end
end
Bar.new.saikyo # => You're not!
げげ、やっぱり!
特異メソッドを使う
インスタンスに定義された特異メソッドって優先順位が高かったような気がするから、やってみよう。
いちいちインスタンス化したあとにメソッド定義をするのは面倒だから…そうだ、イニシャライザでやっちゃえ!
class Foo
def initialize
instance_eval do
def saikyo
puts "I'm saikyo!"
end
end
end
end
class Bar < Foo
def saikyo
puts "You're not!"
end
end
Bar.new.saikyo # => I'm saikyo!
勝った、勝ったぞ!
しかし、これだと同じトリックを子クラスでやられると…
class Foo
def initialize
instance_eval do
def saikyo
puts "I'm saikyo!"
end
end
end
end
class Bar < Foo
def initialize
instance_eval do
def saikyo
puts "You're not!"
end
end
end
end
Bar.new.saikyo # => You're not!
子クラスでのイニシャライザを叩き潰す
やはりか…
こうなったらメソッド定義に介入するしかないな…
幸い(?)Rubyにはクラス継承とメソッド定義に対するフックを持ってる。これらを駆使すれば、子クラスでのメソッド定義を禁止できるはず!
class Foo
def self.inherited(subclass)
already_defined = false
subclass.define_singleton_method(:method_added) do |method_name|
return if already_defined
if method_name == :initialize
already_defined = true
define_method(method_name) { super() }
end
end
end
def initialize
instance_eval do
def saikyo
puts "I'm saikyo!"
end
end
end
end
class Bar < Foo
def initialize
instance_eval do
def saikyo
puts "You're not!"
end
end
end
end
Bar.new.saikyo # => I'm saikyo!
解説は諦めた、各自読んでくれ!
でも、ここまでしても、インスタンスに直接定義された特異メソッドを上書きすることはできなかったよ…
まとめ
こんなRubyハックにマジになっちゃってどうするの