I'm guessing this is just for debugging purposes, eg you are putting sleep statements in for testing, and don't want them in when you commit. If that is the case, the below code does what you want:
require 'ripper'
class MethodParser
def initialize(source)
@ast = Ripper.sexp(source)
end
def is_method_called?(method_name)
search_ast_for_method(@ast, method_name)
end
private
def is_top_level_method_call(ast, method_name)
# firstly check if possible command block
unless ast.is_a?(Array) && ast.length > 1 && ast[1].is_a?(Array)
return false
end
# now check if it is a function call or command, and check the method name
if [:command, :fcall].include? ast[0]
ast[1].include?(method_name.to_s)
else
false
end
end
def search_ast_for_method(ast, method_name)
return true if is_top_level_method_call(ast, method_name)
return false unless ast.is_a? Array
ast.any? { |e| search_ast_for_method(e, method_name) }
end
end
Example usage:
>> m = MethodParser.new <<EOF
class TestClass
def method
puts "hello"
sleep(42)
end
end
EOF
=> #<MethodParser:0x007f9df3a493c0 @ast=[:program, [[:class, [:const_ref, [:@const, "TestClass", [1, 6]]], nil, [:bodystmt, [[:def, [:@ident, "method", [2, 6]], [:params, nil, nil, nil, nil, nil, nil, nil], [:bodystmt, [[:command, [:@ident, "puts", [3, 4]], [:args_add_block, [[:string_literal, [:string_content, [:@tstring_content, "hello", [3, 10]]]]], false]], [:method_add_arg, [:fcall, [:@ident, "sleep", [4, 4]]], [:arg_paren, [:args_add_block, [[:@int, "42", [4, 10]]], false]]]], nil, nil, nil]]], nil, nil, nil]]]]>
>> m.is_method_called? :sleep
=> true
>> m.is_method_called? :puts
=> true
>> m.is_method_called? :hello
=> false
>> m.is_method_called? "hello"
=> false
Note that any dynamic method invocation, or just method aliasing will bypass this, eg eval("sl" + "eep 4")
, or send(:sleep, 4)
. If it is just sanity testing committed code though it should suffice.
Finally it doesn't detect the sleep in Kernel.sleep 4
, although it wouldn't be hard to fix that if need that.
sleep
to a different method and then call that method. You don't need to detect that?