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 valueQuackSet
gets called when you set a property valueQuackInvoke
: gets called when you invoke a method
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
Why Do Ruby People Say They Don't Need Interfaces
Rails Has_Many Through Form with Additional Attributes
How to Automatically Escape HTML Content Using Jekyll and Markdown
Problems While Making a Generic Model in Ruby on Rails 3
Ruby How to Generate a Tree Structure Form Array
Include Module in All Minitest Tests Like in Rspec
Rails3 Scope for Count of Children in Has_Many Relationship
Escaping Strings for Ruby Sqlite Insert
Static Page Routing in Sinatra (Ruby)
Get Value from String Representing Local Variable
Bundler Using Wrong Ruby Version
What Is Toplevel_Binding in Ruby
Sinatra Not Persisting Session with Redirect on Chrome
Sinatra on Nginx Configuration - What's Wrong
Browsing Ruby Code a La Smalltalk
How Does Ruby's Sort Method Work with The Combined Comparison (Spaceship) Operator
Sqlite3::Sqlexception: Duplicate Column Name While Migrating