ブロックなしでメソッドが呼び出されたときにトリガーされるrubocop copを作成します。
概要
私は (これらのガイドラインに基づいて) 新しい警官を開発しようとしており、正しいノード パターンを取得しようと頭を悩ませています。
ブロックを提供せずに X.some_method が呼び出されたときに警官に犯罪を登録させたいと考えています。 つまり、 X.some_method は違反ですが、 X.some_method { blah } は違反ではありません。
X.some_method を識別するための正しいパターン、「(send (const nil? :X) :some_method …」を取得しました。 しかし、「ブロックが与えられない」パターンを作成する方法がわからないですか?
解決策
どうやら、解析された AST では、ノードにブロックが与えられると、そのノードはそのブロックの最初の子として表されます。 つまり
[92] pry(RuboCop)> node # `X.some_method(something) { do_something }`
=> s(:block,
s(:send,
s(:const, nil, :X), :some_method,
s(:send, nil, :something)),
s(:args),
s(:send, nil, :do_something))
そして、Rubocop::AST インスタンスを使用してそれを確認できます。 完全な実装は次のとおりです (複数のメソッド名のオプションを含みます)。
MSG = 'Avoid using `X.%<method>s` without providing a block.'
def_node_matcher :x_method, '(send (const nil? :X) ${:some_method :another_method} ...)'
def on_send(node)
x_method(node) do |method_name|
return if !method_name || first_child_of_block?(node)
add_offense(node, location: :selector, message: format(MSG, method: method_name))
end
end
private
# checks if the given node's parent is a block, and the given node is its first child,
# which would mean that the block is supplied to the given node (i.e `node { block }`)
def first_child_of_block?(node)
return false unless (parent = node.parent)
return false unless parent.type == :block
parent.children.first == node
end