After many years in Ruby, I am still fascinated by the flexibility, simplicity, and power of `IRB` and `rails console`. Even with IDEs getting better, I like the experience of using IRB or rails console to explore the code.

In this article, I will show how to find the source where a method is defined.

There are three ways to find out where a method is defined in Ruby using IRB:

* using the [`source_location`](https://docs.ruby-lang.org/en/3.2/Method.html#method-i-source_location) defined on the Method object
    
* using the IRB `show_source` command
    
* displaying information about an object using `ls` (for displaying the object but not the source file)
    

Before we start, here is my IRB configuration:

![](/assets/images/posts/finding-where-a-method-is-defined-in-ruby-using-irb/134180f6-02d3-49e0-a800-5419188ca747.png)

```ruby
# .irbrc content
# I use the Dracula theme for IRB from https://github.com/katsyoshi/irb-theme-dracula
require "irb/theme/dracula/light"
```

All code executed below is run inside `irb`.

## A simple Ruby script

Let's assume the following object:

```ruby
# frozen_string_literal: true

module Universe
  module Galaxy
    class Star
      attr_reader :name
      attr_writer :name

      def initialize(name)
        @name = name
      end
    end
  end
end
```

Now let's start IRB and instantiate an object out of it:

```ruby
require_relative "universe/galaxy/star"
star = Universe::Galaxy::Star.new("Some Other Sun")
```

### Finding available methods and instance variables

When in IRB you can use `ls` to display the available methods and instance variables:

```ruby
ls star
# => 
# Universe::Galaxy::Star#methods: name  name=
# instance variables: @name
```

While this does not provide a source file and a line number you will see in examples below that it can correctly identify in case of inheritance for example where a method is defined.

### Finding the source using `Method#source_location`

Executing `star.method(:name).source_location` will return the proper source location with the specific line number:

```ruby
star.method(:name) # This will return a Method object
star.method(:name).source_location 

# => 
# ["universe/galaxy/star.rb", 6]
```

What about the setter? It will work in this case too:

```ruby
star.method(:name=).source_location 
# => 
# ["universe/galaxy/star.rb", 7]
```

### Finding the source using `show_source` from IRB

When in IRB you can use `show_source` to display the source for a method:

```ruby
show_source star.name
```

The response will be:

```ruby
From: universe/galaxy/star.rb:6

      attr_reader :name
      attr_writer :name
```

This will work for the setter too so executing the following command:

```ruby
show_source star.name=
```

Will return the following response:

```ruby
From: universe/galaxy/star.rb:7

      attr_writer :name
```

## A more complex Ruby script

What if we add inheritance? How will discovering the source work in this case?  
Let's now create a new class:

```ruby
module Universe
  module Galaxy
    class SimpleSun < Star
      def initialize
        super('Sun')
      end

      def our_planet
        Planet.new('Earth')
      end

      def name
        puts "SimpleSun#{@name}"
      end
    end
  end
end
```

Let's load this in an IRB session:

```ruby
require_relative "universe/galaxy/simple_sun"
simple_sun = Universe::Galaxy::SimpleSun.new
```

### Finding available methods and instance variables

Let's explore with `ls`:

```ruby
ls simple_sun
# => 
# Universe::Galaxy::SimpleSun#methods: name  our_planet
# Universe::Galaxy::Star#methods: name=
# instance variables: @name
```

Notice that it correctly showed that `name` is from `Star` and was not overwritten in the SimpleSun while `name=` is correctly identified as being from `Universe::Galaxy::Star`

### Finding source location with `Method#source_location`

Using `source_location` on `name` will show the source being in `simple_sun.rb` while using `source_location` on `name=` will show the source being `star.rb`:

```ruby
simple_sun.method(:name).source_location
# => 
# ["universe/galaxy/simple_sun.rb", 14]

simple_sun.method(:name=).source_location
# =>
# ["universe/galaxy/star.rb", 7]
```

### Finding source location with `show_source` IRB command

Using `show_source` will correctly display the source code for both `name` and for `name=` (inherited from `Star`).

Executing:

```ruby
show_source simple_sun.name
```

Will return the following response:

```ruby
From: universe/galaxy/simple_sun.rb:14

      def name
        puts "SimpleSun#{@name}"
      end
```

While executing:

```ruby
show_source simple_sun.name=
```

Will return the following response:

```ruby
From: universe/galaxy/star.rb:7

      attr_writer :name
```

### An extra option for `show_source`

Let's execute again the `show_source` but this time on the initializer where in `SimpleSun` I wrote `super('Sun')` so I am calling super with an argument:

```ruby
show_source Universe::Galaxy::SimpleSun#initialize
```

This will return:

```ruby
From: universe/galaxy/simple_sun.rb:6

      def initialize
        super('Sun')
      end
```

What if you want to see the `super` method? `show_source` supports a flag `-s` that will help displaying the super class method. You can stack as many `-s` as you like eg. `-sssss`

In our case there is one single super added there so I will execute:

```ruby
show_source Universe::Galaxy::SimpleSun#initialize -s
```

And this will return:

```ruby
From: universe/galaxy/star.rb:9

      def initialize(name)
        @name = name
      end
```

## A Ruby script using `define_method`

Now let's complicate a bit the things and define methods dynamically with `define_method`:

```ruby
require_relative "star"

module Universe
  module Galaxy
    class TwinSun < Star
      NAME_ALTERNATIVES = %i(twin_helios twin_sun twin_sol twin_solis)

      def initialize
        super('TwinSun')
      end

      def our_planet
        'Twin Earth'
      end

      NAME_ALTERNATIVES.each do |name|
        define_method "#{name}?" do
          true
        end

        define_method name do
          name
        end
      end
    end
  end
end
```

With our new class defined, let's start the IRB and instantiate `twin_sun`:

```ruby
require_relative "universe/galaxy/twin_sun"
twin_sun = Universe::Galaxy::TwinSun.new
```

### Finding available methods and instance variables

And we can explore with `ls`:

```ruby
ls twin_sun
# => 
# Universe::Galaxy::TwinSun#methods:
#   our_planet  twin_helios  twin_helios?  twin_sol  twin_sol?  twin_solis  twin_solis?  twin_sun  twin_sun?
# Universe::Galaxy::Star#methods: name  name=
# instance variables: @name
```

You can notice that it shows all the defined method too when doing `ls twin_sun`.

`ls` also correctly identifies that `name` and `name=` are from the parent `Star` object.

### Finding source location with `Method#source_location`

What about `source_location`?

Let's first try a method defined directly on the `TwinSun` and notice it shows the file and the line number:

```ruby
twin_sun.method(:our_planet).source_location
# => 
# ["universe/galaxy/twin_sun.rb", 12]
```

Now let's try to identify one of the methods created with `define_method`:

```ruby
twin_sun.method(:twin_sun?).source_location
# => 
# ["universe/galaxy/twin_sun.rb", 17]
```

And opening that directly from IRB with `edit "universe/galaxy/twin_sun.rb"` will show the exact line where `define_method #{name}?` is written:

![](/assets/images/posts/finding-where-a-method-is-defined-in-ruby-using-irb/770fad4e-0b26-4cb5-a27c-c3d5aca6b83d.png)

### Finding source location with `show_source` IRB command

If you want to directly get a glimpse in IRB of the source code, you can use `show_source`

```ruby
show_source twin_sun.twin_sun?
```

The result will be:

```ruby
From: universe/galaxy/twin_sun.rb:17

        define_method "#{name}?" do
          true
        end
```

This is super cool =&gt; it is showing the place where `define_method` is used to create the `twin_sun?` method:

## Rails

I am using for this a newly generated Rails app (using Ruby 3.2.2 and Rails 7.1) where I just added two models. They are very simple:

```ruby
# db/schema.db

create_table "galaxies", force: :cascade do |t|
  t.string "name"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "stars", force: :cascade do |t|
  t.string "name"
  t.integer "galaxy_id", null: false
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.index ["galaxy_id"], name: "index_stars_on_galaxy_id"
end

add_foreign_key "stars", "galaxies"

# app/models/galaxy.rb

class Galaxy < ApplicationRecord
  has_many :stars
end

# app/models/star.rb 

class Star < ApplicationRecord
  belongs_to :galaxy
end
```

Let's start the Rails console with `rails c` and try to find the source code or location for some methods.

Assume there is already a record for Galaxy with name `Andromeda` and a couple of stars in that galaxy: `Alpheratz` and `Mirach`.

### **Finding using** `source_location`

We might want to load the galaxy with name `Andromeda` by using `Galaxy.find_by` but where is that method defined?

```ruby
Galaxy.method(:find_by).source_location
# => 
# [".gem/ruby/3.2.2/gems/activerecord-7.1.2/lib/active_record/core.rb", 256]
```

And if we open that file, we will get:

![](/assets/images/posts/finding-where-a-method-is-defined-in-ruby-using-irb/b9479a36-5348-467e-8c9b-c7ba35bd141d.png)

What about `exists?` from Active Record? That works too indicating where `exists?` is defined:

```ruby
Galaxy.method(:exists?).source_location
# => 
# [".gem/ruby/3.2.2/gems/activerecord-7.1.2/lib/active_record/querying.rb", 23]
```

### **Finding the source using** `show_source`

```ruby
show_source Galaxy.find_by
```

The result of executing that line will be:

```ruby
From: .gem/ruby/3.2.2/gems/activerecord-7.1.2/lib/active_record/core.rb:256

      def find_by(*args) # :nodoc:
        return super if scope_attributes?

        hash = args.first
        return super unless Hash === hash

        hash = hash.each_with_object({}) do |(key, value), h|

# ...
```

I only included here the first few lines from the `find_by` method but you can notice that it correctly displayed the source code.

### Finding source code for `params` in the Rails controller

I generated a simple scaffolding for Galaxies and I will use the `debug` gem (included by default in Rails 7.1) to start an IRB session in the context of the `index` action:

```ruby
# app/controllers/galaxies_controller.rb

class GalaxiesController < ApplicationController
  before_action :set_galaxy, only: %i[ show edit update destroy ]

  # GET /galaxies or /galaxies.json
  def index
    binding.irb
    @galaxies = Galaxy.all
  end

# ... more code
```

#### Using `source_location`

Knowing that params is a method (can be easily checked with `self.methods.include?(:params)` inside the action) I can do the following:

```ruby
self.method(:params).source_location # works also without self
# => 

[".gem/ruby/3.2.2/gems/actionpack-7.1.2/lib/action_controller/metal/strong_parameters.rb", 1281]
```

#### Using `show_source`

```ruby
show_source params
# => 

From: .gem/ruby/3.2.2/gems/actionpack-7.1.2/lib/action_controller/metal/strong_parameters.rb:1281

    def params
      @_params ||= begin
        context = {
          controller: self.class.name,
          action: action_name,
          request: request,
          params: request.filtered_parameters
        }
        Parameters.new(request.parameters, context)
      end
    end
```

#### Exploring `params` with ls

You can find out more information about `params` if you use `ls` command:

```ruby
irb(#<GalaxiesController:0x000000...):004> ls params
ActionController::Parameters#methods:
  ==                    []               []=               always_permitted_parameters  always_permitted_parameters=  as_json      compact
  compact!              compact_blank    compact_blank!    converted_arrays             deep_dup                      deep_merge?  deep_transform_keys
  deep_transform_keys!  delete           delete_if         dig                          each                          each_key     each_pair
  each_value            empty?           encode_with       eql?                         except                        exclude?     extract!
  extract_value         fetch            has_key?          has_value?                   hash                          include?     init_with
  inspect               keep_if          key?              keys                         member?                       merge        merge!
  permit                permit!          permitted?        reject                       reject!                       require      required
  reverse_merge         reverse_merge!   select            select!                      slice                         slice!       stringify_keys
  to_h                  to_hash          to_param          to_query                     to_s                          to_unsafe_h  to_unsafe_hash
  transform_keys        transform_keys!  transform_values  transform_values!            value?                        values       values_at
  with_defaults         with_defaults!   without
ActiveSupport::DeepMergeable#methods: deep_merge  deep_merge!
instance variables: @logging_context  @parameters  @permitted
class variables:
  @@action_on_unpermitted_parameters  @@allow_deprecated_parameters_hash_equality  @@always_permitted_parameters  @@permit_all_parameters
```

## When `show_source` and `source_location` does not work

### When methods are defined with `method_missing`

In case you have your behavior defined with `method_missing` then `source_location` and `show_source` will not work:

```ruby
# frozen_string_literal: true

module Universe
  module Galaxy
    class Sun < Star
      DYNAMIC_METHODS = %i(life?).freeze

      def initialize
        super('Sun')
      end

      def our_planet
        'Earth'
      end

      private

      def respond_to_missing?(name, include_private = false)
        DYNAMIC_METHODS.include?(name) || super
      end

      def method_missing(name, *args)
        return super unless DYNAMIC_METHODS.include?(name)

        if name == :life?
          return true
        end
      end
    end
  end
end
```

In this case if you load this into IRB:

```ruby
require_relative "universe/galaxy/sun"
sun = Universe::Galaxy::Sun.new
```

You can notice that you can execute `sun.life?` and even check `sun.respond_to?(:life?)` :

```ruby
sun.life? # => true
sun.respond_to?(:life?) # => true
```

You will then notice that `life?` is not a method when investigating with:

```ruby
sun.methods.include?(:life?) # => false
sun.private_methods.include?(:life?) # => false
Universe::Galaxy::Sun.public_methods.include?(:life?) #  false
Universe::Galaxy::Sun.protected_methods.include?(:life?) # false
Universe::Galaxy::Sun.private_methods.include?(:life?) # false 
Universe::Galaxy::Sun.singleton_methods.include?(:life?) # false
Universe::Galaxy::Sun.instance_methods.include?(:life?) # false
```

You can still get the `Method` object for it:

```ruby
sun.method(:life?)
# => #<Method: Universe::Galaxy::Sun#life?(*)>
```

But `source_location` or `show_source` will not work:

```ruby
sun.method(:life?).source_location
# => nil

show_source sun.life?
# => Error: Couldn't locate a definition for sun.life?
```

It is also not displayed when using `ls` on the `sun` object:

```ruby
ls sun
# => 
# Universe::Galaxy::Sun#methods: our_planet
# Universe::Galaxy::Star#methods: name  name=
# instance variables: @name
```

### On the Kernel, BasicObject and Object methods

In general `source_location` and `show_source` will show you the Ruby code that defined a method, but will not show you any C code behind any method that you are using in Ruby.

So for example the following will not work:

```ruby
# respond_to? is inherited from Object

sun.method(:respond_to?)
# => #<Method: Universe::Galaxy::Sun(Kernel)#respond_to?(*)>

show_source sun.respond_to?
# => Error: Couldn't locate a definition for sun.respond_to?

sun.method(:respond_to?).source_location
# => nil

# == is inherited from BasicObject 

sun.method(:==) 
# => #<Method: Universe::Galaxy::Sun(BasicObject)#==(_)>

sun.method(:==).source_location
# => nil

show_source sun.==
# Error: Couldn't locate a definition for sun.==
```

If you want to make this work you have to use Ruby [TracePoint](https://docs.ruby-lang.org/en/3.3/TracePoint.html) but that is out of the scope of this article.

## Conclusion

I think Ruby has now very good tools to navigate to the source of a method either from the class or from the instantiated object. Using `IRB` and `debug` gem makes this navigation easy, and I would recommend anyone learning Ruby to keep an `irb` or `rails console` session open. That way, whenever you have a question about how some particular method works, you can navigate to the source of that method and read the code.

---

*I would like to thank* [*Stephen Margheim*](https://fractaledmind.github.io) *and* [*Stan Lo*](https://st0012.dev) *for reviewing an early version of this article and providing valuable feedback.*

---

Enjoyed this article?

👉 Join my [**Short Ruby News**](https://shortruby.com/) **newsletter** for weekly Ruby updates from the community and visit [**rubyandrails.info**](http://rubyandrails.info)**,** a directory with learning content about Ruby.

👐 Subscribe to my Ruby and Ruby on rails courses over email at [learn.shortruby.com](https://learn.shortruby.com) - effortless learning anytime, anywhere

🤝 Let's connect on [**Ruby.social**](https://ruby.social/@lucian) or [**Linkedin**](https://linkedin.com/in/lucianghinda) or [**Twitter**](https://x.com/lucianghinda) where I post mainly about Ruby and Rails.

🎥 Follow me on [**my YouTube channel**](https://www.youtube.com/@shortruby) for short videos about Ruby

---
Published at: 2024-01-11 | Updated at: 2024-01-12 | Author: Lucian Ghinda
