1

Ruby コードを解析し、特定の関数呼び出しの存在を検出するかどうかを報告するために使用できる gem を書きたいと考えています。sleep私の特定の使用例は、呼び出しを含むメイン コードベースにコミットがマージされないようにすることです。

これを行う強引な方法は、プレーンテキストファイルを解析し、コメントされていない行で「スリープ」を探すことですが、エラーが発生しやすく、やや醜いことも想像できます。

Rubyコードで特定の関数呼び出しを検索する方法はありますか?おそらくコードをある種のトークン化された形式に「コンパイル」し、それを解析しますか?

4

1 に答える 1

2

これはデバッグ目的のためだけだと思います。たとえば、テストのためにスリープステートメントを入れていて、コミット時にそれらを入れたくない場合などです。その場合、以下のコードはあなたが望むことをします:

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

使用例:

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

動的なメソッドの呼び出し、または単にメソッドのエイリアシングはこれをバイパスすることに注意してeval("sl" + "eep 4")くださいsend(:sleep, 4)。コミットされたコードの健全性テストだけであれば、それで十分です。

最後にKernel.sleep 4、必要に応じて修正するのは難しくありませんが、でスリープを検出しません。

于 2013-06-04T13:22:44.177 に答える