By Matt Rogish


2008-10-06 18:44:24 8 Comments

We recently had a problem where, after a series of commits had occurred, a backend process failed to run. Now, we were good little boys and girls and ran rake test after every check-in but, due to some oddities in Rails' library loading, it only occurred when we ran it directly from Mongrel in production mode.

I tracked the bug down and it was due to a new Rails gem overwriting a method in the String class in a way that broke one narrow use in the runtime Rails code.

Anyway, long story short, is there a way, at runtime, to ask Ruby where a method has been defined? Something like whereami( :foo ) that returns /path/to/some/file.rb line #45? In this case, telling me that it was defined in class String would be unhelpful, because it was overloaded by some library.

I cannot guarantee the source lives in my project, so grepping for 'def foo' won't necessarily give me what I need, not to mention if I have many def foo's, sometimes I don't know until runtime which one I may be using.

10 comments

@wesgarrison 2009-03-18 21:12:44

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

@Jeff Maass 2012-07-17 18:03:33

source_location appears to work for 1.8.7-p334 using activesupport-2.3.14

@Juguang 2013-12-03 11:20:27

after finding the method, try Method's owner method

@stack1 2015-03-02 21:24:47

What is the number two in 2.method(:crime) ?

@kitteehh 2015-05-01 08:01:23

an instance of the class Fixnum

@cdpalmer 2015-05-06 21:37:46

Important note: This will not pull up any dynamically defined methods from method_missing. So if you have a module or ancestor class with a class_eval or define_method inside of method_missing, then this method won't work.

@devius 2016-03-25 14:41:40

What if there's no class or instance? e.g. def something() create :stuff end, how can I find out where create is being defined?

@Jwan622 2016-05-19 14:10:37

But really why is there a 2 there?

@Obromios 2017-03-04 03:46:55

In reply to devius's comment, try self(:create).source_location or even method(:create).self_location

@Samda 2016-07-14 07:51:48

Maybe the #source_location can help to find where is the method come from.

ex:

ModelName.method(:has_one).source_location

Return

[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]

OR

ModelName.new.method(:valid?).source_location

Return

[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]

@James Adam 2010-08-25 09:52:04

You can actually go a bit further than the solution above. For Ruby 1.8 Enterprise Edition, there is the __file__ and __line__ methods on Method instances:

require 'rubygems'
require 'activesupport'

m = 2.days.method(:ago)
# => #<Method: Fixnum(ActiveSupport::CoreExtensions::Numeric::Time)#ago>

m.__file__
# => "/Users/james/.rvm/gems/ree-1.8.7-2010.01/gems/activesupport-2.3.8/lib/active_support/core_ext/numeric/time.rb"
m.__line__
# => 64

For Ruby 1.9 and beyond, there is source_location (thanks Jonathan!):

require 'active_support/all'
m = 2.days.method(:ago)
# => #<Method: Fixnum(Numeric)#ago>    # comes from the Numeric module

m.source_location   # show file and line
# => ["/var/lib/gems/1.9.1/gems/activesupport-3.0.6/.../numeric/time.rb", 63]

@Vikrant Chaudhary 2011-01-28 06:42:32

I get "NoMethodError: undefined method" for both __file__ and __line__ on any Method class instance, ex: method(:method).__file__.

@James Adam 2011-02-15 20:53:30

Which version of ruby do you have?

@Vikrant Chaudhary 2011-02-23 11:26:25

ruby 1.8.7 (2010-06-23 patchlevel 299) [x86_64-linux]

@Jonathan Tran 2011-03-03 01:15:41

On Ruby 1.9, m.__file__ and m.__line__ have been replaced with m.source_location.

@so_mv 2011-11-04 19:59:47

file and line do not work in Jruby 1.5.2

@Lokesh 2015-12-22 17:33:20

What about Ruby 2.1?

@James Adam 2016-01-05 17:09:13

source_location works with Ruby 1.9 and upwards, including 2.1

@Garry 2008-10-07 05:07:14

You can always get a backtrace of where you are by using caller().

@the Tin Man 2012-10-22 15:21:16

This is useful to find what called you, but not good when you're trying to find where something was defined.

@Laas 2012-10-22 16:29:23

Copying my answer from a newer similar question that adds new information to this problem.

Ruby 1.9 has method called source_location:

Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native)

This has been backported to 1.8.7 by this gem:

So you can request for the method:

m = Foo::Bar.method(:create)

And then ask for the source_location of that method:

m.source_location

This will return an array with filename and line number. E.g for ActiveRecord::Base#validates this returns:

ActiveRecord::Base.method(:validates).source_location
# => ["/Users/laas/.rvm/gems/[email protected]/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

For classes and modules, Ruby does not offer built in support, but there is an excellent Gist out there that builds upon source_location to return file for a given method or first file for a class if no method was specified:

In action:

where_is(ActiveRecord::Base, :validates)

# => ["/Users/laas/.rvm/gems/[email protected]/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

On Macs with TextMate installed, this also pops up the editor at the specified location.

@Orion Edwards 2008-10-06 20:01:50

If you can crash the method, you'll get a backtrace which will tell you exactly where it is.

Unfortunately, if you can't crash it then you can't find out where it has been defined. If you attempt to monkey with the method by overwriting it or overriding it, then any crash will come from your overwritten or overridden method, and it won't be any use.

Useful ways of crashing methods:

  1. Pass nil where it forbids it - a lot of the time the method will raise an ArgumentError or the ever-present NoMethodError on a nil class.
  2. If you have inside knowledge of the method, and you know that the method in turn calls some other method, then you can overrwrite the other method, and raise inside that.

@the Tin Man 2012-10-22 15:23:52

If you have access to the code, you can just as easily insert require 'ruby-debug'; debugger in your code where you want to drop in.

@Alex D 2012-02-20 04:07:21

I'm coming late to this thread, and am surprised that nobody mentioned Method#owner.

class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A

@fny 2012-06-04 20:15:29

I'm surprised that you're the first to reference the Method class explicitly. Another lesser known treasure introduced in 1.9: Method#parameters.

@tig 2010-05-14 18:36:18

Very late answer :) But earlier answers did not help me

set_trace_func proc{ |event, file, line, id, binding, classname|
  printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}
# call your method
set_trace_func nil

@Arup Rakshit 2014-01-09 05:56:56

Why are you passing nil ?

@tig 2014-01-14 18:17:33

@ArupRakshit from documentation: «Establishes proc as the handler for tracing, or disables tracing if the parameter is nil

@AShelly 2008-10-06 20:16:47

You might be able to do something like this:

foo_finder.rb:

 class String
   def String.method_added(name)
     if (name==:foo)
        puts "defining #{name} in:\n\t"
        puts caller.join("\n\t")
     end
   end
 end

Then ensure foo_finder is loaded first with something like

ruby -r foo_finder.rb railsapp

(I've only messed with rails, so I don't know exactly, but I imagine there's a way to start it sort of like this.)

This will show you all the re-definitions of String#foo. With a little meta-programming, you could generalize it for whatever function you want. But it does need to be loaded BEFORE the file that actually does the re-definition.

@Ken 2008-10-06 20:01:29

This may help but you would have to code it yourself. Pasted from the blog:

Ruby provides a method_added() callback that is invoked every time a method is added or redefined within a class. It’s part of the Module class, and every Class is a Module. There are also two related callbacks called method_removed() and method_undefined().

http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby

Related Questions

Sponsored Content

23 Answered Questions

[SOLVED] How to write a switch statement in Ruby

10 Answered Questions

[SOLVED] Pass Method as Parameter using C#

14 Answered Questions

[SOLVED] Checking if a variable is defined?

  • 2008-11-13 23:14:05
  • Readonly
  • 316744 View
  • 561 Score
  • 14 Answer
  • Tags:   ruby reflection

26 Answered Questions

[SOLVED] How can I rename a database column in a Ruby on Rails migration?

31 Answered Questions

[SOLVED] What's the difference between a method and a function?

24 Answered Questions

[SOLVED] Runtime vs Compile time

18 Answered Questions

[SOLVED] How to get a random number in Ruby

33 Answered Questions

[SOLVED] How do I get the current absolute URL in Ruby on Rails?

10 Answered Questions

[SOLVED] Why are exclamation marks used in Ruby methods?

12 Answered Questions

[SOLVED] How to make --no-ri --no-rdoc the default for gem install?

  • 2009-09-04 21:48:59
  • Ricardo Acras
  • 226428 View
  • 990 Score
  • 12 Answer
  • Tags:   ruby rubygems

Sponsored Content