How to Find Out What Is Intercepting 'Method_Missing'

How to find out what is intercepting 'method_missing'

I think @Sebi's answer is helpful, but I'd like to improve it like this:

set_trace_func proc { |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname if id.to_s == 'method_missing'
}

The result is like this:

ruby-1.8.7-p334 :036 > SomeModel.some_missing_method
call /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1873 method_missing ActiveRecord::Base
line /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1874 method_missing ActiveRecord::Base
line /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1874 method_missing ActiveRecord::Base
line /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1981 method_missing ActiveRecord::Base
line /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing ActiveRecord::Base
c-call /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing Kernel
raise /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing ActiveRecord::Base
c-return /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing Kernel
return /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing ActiveRecord::Base

method_missing visibility in Ruby

It is not the private method of Object that is called but the module method in Kernel. You can check which method is called with set_trace_func as described in the answer to a similar question:

irb(main):001:1> set_trace_func proc { |event, file, line, id, binding, classname|   printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname if id.to_s == 'method_missing' }
=> #<Proc:0x0423d278@(irb):1>
irb(main):002:0> Object.method_missing :test
c-call (irb):4 method_missing Kernel
c-return (irb):4 method_missing Kernel
NoMethodError: undefined method `test' for Object:Class
from (irb):4
from :0

As some commenters pointed out in MRIs newer than 1.8.7 this behaviour has changed: method_missing has been removed from Kernel and the private instance method from Object was moved to BasicObject which is the new superclass.

method_missing in Programming Ruby over my head

Ruby doesn't have any type enforcement, and likewise doesn't do any checking as to what methods an object has when the script is first parsed, because this can be dynamically changed as the application runs.

What method_missing does, is let you intercept and handle calls to methods that don't exist for a given object. This provides the under-the-hood power behind pretty much every DSL (domain-specific language) written in Ruby.

In the case of the example, every one of 'r.iv', 'r.mm', and so on is actually a method call to the Roman object. Of course, it doesn't have an 'iv' or an 'mm' method, so instead control is passed to method_missing, which gets the name of the method that was called, as well as whatever arguments were passed.

method_missing then converts the method name from a symbol to a string, and parses it as a Roman number, returning the output as an integer.

Python equivalent of Ruby's 'method_missing'

There is no difference in Python between properties and methods. A method is just a property, whose type is just instancemethod, that happens to be callable (supports __call__).

If you want to implement this, your __getattr__ method should return a function (a lambda or a regular def, whatever suite your needs) and maybe check something after the call.

Ruby’s “method_missing” in Python

As others have mentioned, in Python, when you execute o.f(x), it's really a two-step operation: First, get the f attribute of o, then call it with parameter x. It's the first step that fails because there is no attribute f, and it's that step that invokes the Python magic method __getattr__.

So you have to implement __getattr__, and what it returns must be callable. Keep in mind, if you also try to get o.some_data_that_doesnt_exist, the same __getattr__ will be called, and it won't know that it's a "data" attribute vs. a "method" that being sought.

Here's an example of returning a callable:

class MyRubylikeThing(object):
#...

def __getattr__(self, name):
def _missing(*args, **kwargs):
print "A missing method was called."
print "The object was %r, the method was %r. " % (self, name)
print "It was called with %r and %r as arguments" % (args, kwargs)
return _missing

r = MyRubylikeThing()
r.hello("there", "world", also="bye")

produces:

A missing method was called.
The object was <__main__.MyRubylikeThing object at 0x01FA5940>, the method was 'hello'.
It was called with ('there', 'world') and {'also': 'bye'} as arguments

How to find where a method is defined at runtime?

This is really late, but here's how you can find where a method is defined:

http://gist.github.com/76951

# How to find out where a method comes from.
# Learned this from Dave Thomas while teaching Advanced Ruby Studio
# Makes the case for separating method definitions into
# modules, especially when enhancing built-in classes.
module Perpetrator
def crime
end
end

class Fixnum
include Perpetrator
end

p 2.method(:crime) # The "2" here is an instance of Fixnum.
#<Method: Fixnum(Perpetrator)#crime>

If you're on Ruby 1.9+, you can use source_location

require 'csv'

p CSV.new('string').method(:flock)
# => #<Method: CSV#flock>

CSV.new('string').method(:flock).source_location
# => ["/path/to/ruby/1.9.2-p290/lib/ruby/1.9.1/forwardable.rb", 180]

Note that this won't work on everything, like native compiled code. The Method class has some neat functions, too, like Method#owner which returns the file where the method is defined.

EDIT: Also see the __file__ and __line__ and notes for REE in the other answer, they're handy too. -- wg

How do I debug Ruby's method_missing?

A very crude way would be to output the call stack by putting

puts caller

right above your XmlNode... line. That will output the call stack and you can see who called the method.

Ruby any way to catch messages before method_missing?

Not that I know of.

The most performant bet is usually to use method_missing to dynamically add the method being to a called to the class so that the overhead is only ever incurred once. From then on it calls the method like any other method.

Such as:

class Foo
def method_missing(name, str)

# log something out when we call method_missing so we know it only happens once
puts "Defining method named: #{name}"

# Define the new instance method
self.class.class_eval <<-CODE
def #{name}(arg1)
puts 'you passed in: ' + arg1.to_s
end
CODE

# Run the instance method we just created to return the value on this first run
send name, str
end
end

# See if it works
f = Foo.new
f.echo_string 'wtf'
f.echo_string 'hello'
f.echo_string 'yay!'

Which spits out this when run:

Defining method named: echo_string
you passed in: wtf
you passed in: hello
you passed in: yay!

How can I intercept a method call in Boo?

Yes, Boo has IQuackFu.

Basically, you implement IQuackFu, which has three methods:

  • QuackGet: gets called when you get a property value
  • QuackSet gets called when you set a property value
  • QuackInvoke: gets called when you invoke a method

Here's an example.

Does Javascript have something like Ruby's method_missing feature?

The ruby feature that you are explaining is called "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.

It's a brand new feature that is present only in some browsers like Firefox (in the spider monkey Javascript engine). In SpiderMonkey it's called "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod

Please read this article from Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ for more details about the upcoming implementation.



Related Topics



Leave a reply



Submit