18
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-09-08

概要

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

本編

さて、上書きできないメソッド、どうやって定義しようか?
まずは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ハックにマジになっちゃってどうするの

18
10
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?