ActiveRecord Association extensionsでメソッドを追加する - valid,invalid で書いたActiveRecord Association Extensionだが、with_options
と併用するとrubocop-rails
のRails/HasManyOrHasOneDependent
copに警告されることがわかった。
with_options dependent: :destroy do
has_many :foo do
end
end
with_options dependent: :destroy do
has_many :foo do
^^^^^^^^ Specify a `:dependent` option.
end
end
原因調査
disableしてもよいのだがどのような原理で警告が出るのか気になったので調べて修正してみた。
github.com
対象のcopの実装を読みつつprint debugすると、Association Extensionのblockの存在有無によってS式の構造が変わることが原因のようだった。
まず、警告が出ないblockなしのケース。
with_options dependent: :destroy do
has_many :foo
end
s(:block, # <= node.parent
s(:send, nil, :with_options,
s(:hash,
s(:pair,
s(:sym, :dependent),
s(:sym, :destroy)))),
s(:args),
s(:send, nil, :has_many, # <= node
s(:sym, :foo))))
has_many
に対して:dependent
optionが渡されるかどうかはnode.parent
のnodeからwith_options
を辿れば判断できる。
一方、blockありのケース。
with_options dependent: :destroy do
has_many :foo do
end
end
node.parent
のnode下にはwith_options
blockがない。
s(:block, # <= node.parent.parent
s(:send, nil, :with_options,
s(:hash,
s(:pair,
s(:sym, :dependent),
s(:sym, :destroy)))),
s(:args),
s(:block, # <= node.parent
s(:send, nil, :has_many, # <= node
s(:sym, :foo)),
s(:args), nil)))
with_options
の引数を参照するにはnode.parent.parent
から辿らなければいけないのだが、node.parent
が検査対象となってしまう。
def valid_options_in_with_options_block?(node)
return true unless node.parent
n = node.parent.begin_type? ? node.parent.parent : node.parent
contain_valid_options_in_with_options_block?(n)
end
修正
block付きの場合のS式にmatchしたらnode.parent.parent
を参照して検証させるようにした。
+ def_node_matcher :association_extension_block?, <<~PATTERN
+ (block
+ (send nil? :has_many _)
+ (args) ...)
+ PATTERN
def valid_options_in_with_options_block?(node)
return true unless node.parent
- n = node.parent.begin_type? ? node.parent.parent : node.parent
+ n = node.parent.begin_type? || association_extension_block?(node.parent) ? node.parent.parent : node.parent
contain_valid_options_in_with_options_block?(n)
end
rubocop-rails
への初pull requestであり雰囲気で書いているのでacceptされるかはわからない。
(2020-01-13 追記) mergeされた!
これまでrubocopの中身をちゃんと見たことがなかったので学びがあってよかった。
環境
- rubocop 1.8.0
- rubocop-rails 2.9.1
This article is for ohbarye Advent Calendar 2020.