Edited at

Rubyで決して上書きされない最強のメソッドを定義する方法


概要

上書きできない魔法みたいな最強メソッドを定義できるぞ!


本編

さて、上書きできないメソッド、どうやって定義しようか?

まずは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ハックにマジになっちゃってどうするの