19
19

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.

おもしろい method_missing

Last updated at Posted at 2015-06-03

Rails のソースを読んでいると、あらゆるところで method_missing をオーバーライドしています。
例えば、String クラスにある inquiry

env = 'production'.inquiry
env.production?  #=> true
env.development? #=> false

というふうに文字列に #{文字列}? と問い合わせると true/false を返してくれるようになるメソッドですが、実装を見ると

class String
  def inquiry
    ActiveSupport::StringInquirer.new(self)
  end
end

========================================================================

class StringInquirer < String
  private

    def respond_to_missing?(method_name, include_private = false)
      method_name[-1] == '?'
    end

    def method_missing(method_name, *arguments)
      if method_name[-1] == '?'
        self == method_name[0..-2]
      else
        super
      end
    end
end

こんな感じで method_missing を使って定義しています。
method_missing は呼び出したメソッドが存在しないときに、呼ばれるメソッドです。

そのため、例えば env.production? と呼ぶと、そんなメソッドはないので StringInquirermethod_missing が呼ばれます。

すると、一行目の

if method_name[-1] == '?'

true となり(method_name には、呼び出したメソッド名が入ってくる)、

self == method_name[0..-2]

「自分自身」と「? を除いたメソッド名」との比較が行われます。

試しに、? がつかないメソッドを呼んでみると

'production'.inquiry.production #=> NoMethodError

else に入って、祖先クラスの method_missing が呼ばれている事が分かります。

試しに作ってみます。

class String
  private

  def method_missing(method_name, *arguments)
    if /AA\z/ === method_name.to_s 
      puts <<-EOF
        ∧_∧
       ( ・ω・)
       |⊃/(___
      /└-(____/
       ̄ ̄ ̄ ̄ ̄ ̄
      EOF
    else
      super
    end
  end
end

'oyasumi'.to_AA #=>
#        ∧_∧
#       ( ・ω・)
#       |⊃/(___
#      /└-(____/
#       ̄ ̄ ̄ ̄ ̄ ̄

'oyasumi'.toooooooooooo_AA #=>
#        ∧_∧
#       ( ・ω・)
#       |⊃/(___
#      /└-(____/
#       ̄ ̄ ̄ ̄ ̄ ̄

できた!
ただし、これだけではメソッドは「存在していない」ことになっているので、

'oyasumi'.respond_to? :to_AA #=> false

respond_to_missing? をオーバーライドして、メソッド存在させます。

class String
  private

  def respond_to_missing?(method_name, include_private = false)
    /AA\z/ === method_name.to_s 
  end  
end

'oyasumi'.respond_to? :to_AA #=> true

確かに、Stringto_AA というメソッドが存在しています。

こうやって、ライブラリとかは動的にメソッドを作っているんですね。おもしろい!

19
19
0

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
19
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?