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?
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