2

I've been playing with flog lately, its a pretty good tool to generate reports on code complexity for ruby applications. As a result of running flog on a project codebase you get an output that looks similar to this:

  1272.3: flog total
     7.3: flog/method average

    62.2: MyClass#foobar lib/myclass#foobar:123
    ... more similar lines ...

Example above provides a score for a method and references exact line number in the source code where that method is defined. This could be a regular instance/class method or any other "dynamic" method, eg. rake task and such.

So the objective is to extract a snippet of code (most likely a method) from source file that starts with a line number defined in flog output. That snippet then could be used in some web UI to show various code metrics (based on other tools like flay) and/or stored in the database. As far as i can see, this task involves parsing ruby code into AST, and then going through the tree to find corresponding start line and figuring out end line number. I've done some experiments with this library - https://github.com/whitequark/parser, works most of the time, but it's a bit tricky to get a correct result.

Are there any other solutions to quickly extract a method code from a source file written in ruby?

1 Answer 1

5

You can implement feature that find method by name or line number.

This sample code show how to find method code by name. It is dirty, but it works(on Ruby 2.1.2). This use parser(gem install parser).

# method_finder.rb
require 'parser'
require 'parser/current'

class MethodFinder
  def initialize(filename)
@ast = parse(filename)
  end

  def find(method_name)
recursive_search_ast(@ast, method_name)
return @method_source
  end

  private
  def parse(filename)
Parser::CurrentRuby.parse(File.open(filename, "r").read)
  end

  def recursive_search_ast(ast, method_name)
ast.children.each do |child|
  if child.class.to_s == "Parser::AST::Node"
    if (child.type.to_s == "def" or child.type.to_s == "defs") and (child.children[0].to_s == method_name or child.children[1].to_s == method_name)
      @method_source = child.loc.expression.source
    else
      recursive_search_ast(child, method_name)
    end
  end
end
  end
end

And you can use MethodFinder like below.

mf = MethodFinder.new("./method_finder.rb")
puts mf.find("find")

=> def find(method_name)
=>     recursive_search_ast(@ast, method_name)
=>     return @method_source
=>   end
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, i've implemented a very similar solution, except i was matching the block by line number, not a method name.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.