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してもよいのだがどのような原理で警告が出るのか気になったので調べて修正してみた。
対象の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
が検査対象となってしまう。
# https://github.com/rubocop-hq/rubocop-rails/blob/9808efdb80078f1382da7cab8fe0b6a1917af047/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb#L64-L70 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.