In a dynamic language like Ruby, when your classes are including around 10 to 15 modules, having a countless number of ancestors, monkey patching and metaprogramming, it becomes a challenging task for a programmer to debug a code written by someone else. When you are working on a big project and looking into other people’s codebases, you will often spend too much time figuring out where the hell is this method defined. Specially metaprogramming makes it very complicated to debug code because now and then a new method is being declared dynamically without you even knowing it. Here are some cool tricks and shortcuts, I have learned to debug any kind of Ruby code without spending too much time.
Tip:- From this point, every underlined word is attached with a link for some important documentation. Feel free to visit the documentation to understand this better.
Let's use Method class in Ruby.
Now, why do we need Method class? Method class provides us some useful instance methods which we can use to debug. If you want to search for the location of a method you will have to create a Method class object corresponding to that method. Let's take a look at the below code snippet.
Note:- The below code uses #method from Object class in Ruby. Please go through the documentation before reading the code.
Now method #owner tells us the real owner of the method. Owner is the class or a module where the method is defined. This saves you a lot of time in figuring out the method location. Let's take an example of ActiveRecord::Base class in Rails.
From the above code snippet, you can see the #owner method will return the owner of the method which will be a class or a module. For #new method in ActiveRecord::Base, the owner is ActiveRecord::Inheritance::ClassMethods which is a module.
This method gives you the exact location of the method definition in the form of [file_name, line_number]. But this method will give results only for methods which are not part of Ruby standard library. Let’s take some examples.
From the above code snippet, at line 34 a method is being defined dynamically using #define_method. Now looking at the code, it becomes a little confusing that what methods must be defined there.
For methods from the standard ruby library, the #source_location will return nil. So to debug these methods we only have #owner method.
Method #original_name gives us the original name of a method which is useful in the case of #alias_method or #alias_method_chain.
There are many more useful methods provided by Method class. Feel free to explore here:- https://ruby-doc.org/core-2.4.0/Method.html