Seanny123
Seanny123

Reputation: 9336

Get list of classes and methods that call a specific global method

I have a method called $muffinize and I would like to find where it can be found in my code. In other words, given the following code:

class A
    def foo
        $muffinize(1)
    end
    def bar
        ...
    end
end

class B
    def shoop
        $muffinize(2)
    end
    def woop
        ...
    end
end

class C
    def nope
        ...
    end
end

I would like to the result to be (written to a file):

A:foo
B:shoop

I was thinking of accomplishing this with a Regex, but I was wondering if there would be some way of accomplishing this with Ruby meta-programming (which I might be accidentally using as a buzz-word)?

Upvotes: 1

Views: 164

Answers (3)

Anko
Anko

Reputation: 1332

Kernel.caller() will help you show the line number and method that is calling it at runtime. If you put something like puts caller(1,1) in your muffinize function it will output those locations, but only if they are called at runtime.

If you want to do offline source analysis, you need to parse the AST (abstract syntax tree) with something like https://github.com/whitequark/parser.

Here is a quick example with ripper (built into new rubies) - this isn't strictly an AST but it's not extracting classes either

#!/usr/local/env ruby

require 'ripper'
#require 'pry'

contents = File.open('example.rb').read

code = Ripper.lex(contents)
code.each do |line|
    if(line[1] == :on_ident and line[2] == "muffinize")
        puts "muffinize found at line #{line.first.first}"
    end
end

Upvotes: 2

Seanny123
Seanny123

Reputation: 9336

By getting a list of classes and methods via ri, I was then able to analyze each method to retreive their source code using the method_source gem and then searching for muffinize. This does not rule out the possibility of muffinize from appearing in a comment or a string, but I consider the likelihood of this happening to be small enough to ignore.

Upvotes: 0

Jörg W Mittag
Jörg W Mittag

Reputation: 369428

Ignoring the fact that your code isn't even syntactically valid, this is simply not possible.

Here's a simple example:

class A
  def foo
    bar
    muffinize(1)
  end
end

A#foo will call Object#muffinize if and only if bar terminates. Which means that figuring out whether or not A#foo calls Object#muffinize requires to solve the Halting Problem.

Upvotes: 0

Related Questions