Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
19
Help us understand the problem. What is going on with this article?
@pekepek

おもしろい method_missing

More than 5 years have passed since last update.

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
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
pekepek

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
19
Help us understand the problem. What is going on with this article?