Rails のバージョンを上げて belongs_to_required_by_default
を変更したり、 load_defaults
を指定するときに、 belongs_to
に optional: true
をつけるか、古いバージョンでの required: true
相当でも動くようになっている必要があります。
その書き換えに semgrep
を使ってみるとうまくいったので、その紹介です。
動作確認バージョン
- Ruby 2.7.6
- Rails 6.0.6
- semgrep 0.122.0
belongs_to_required_by_default の例
# 古い挙動に戻す
# config.load_defaults 5.0以上の直後に追加
config.active_record.belongs_to_required_by_default = false
# 新しい挙動にする(config.load_defaults 5.0以上と同じ)
# https://github.com/rails/rails/blob/v7.0.4/railties/lib/rails/application/configuration.rb#L100
# config/initializers/new_framework_defaults_*.rb ならこの書き方になっている
Rails.application.config.active_record.belongs_to_required_by_default = true
semgrep での置き換え
試行錯誤して以下の内容のファイルを用意しました。
belongs_to.yaml
rules:
- id: belongs_to_optional
message: 'Add "optional: true" to belongs_to.'
patterns:
- pattern-not: 'belongs_to(..., optional: $V, ...)'
- pattern-not: 'belongs_to(..., required: $V, ...)'
- pattern: 'belongs_to(...)'
fix-regex:
regex: '^([\s\S]+)(\)?)$'
replacement: '\1, optional: true\2'
languages: [ruby]
severity: WARNING
対象の Rails アプリのディレクトリでこのファイルを -f
で指定して semgrep --autofix
を実行すると belongs_to
に optional: true
を追加できました。
$ semgrep -f .../belongs_to.yaml --autofix
苦労した点
- 最初に
synvert
を試そうとしたら、https://synvert.net/ は javascript 用がメインになっているらしく、ruby 用は synvert-ruby になっていました。 -
synvert-ruby
も試しましたが、belongs_to
の引数の途中に改行が入っているときにsynvert-ruby
がエラーで落ちてしまうことがあって、使いにくかったので他の方法を試しました。 -
rubocop
での auto correct も検討しましたが、検出がメインなので、synvert-ruby
以上に書き換えの書き方が難しそうだったので、調査は後回しにしました。 -
semgrep
で最初はfix-regex.regex
で^
をつけていなかったら、なぜか, optional: true, optional: true
と2回追加されていました。
まとめ
semgrep
はパターンの書き方が独特でとっつきにくいですが、パターンを重ねていけるので、 grep
をパイプでつないでいくように簡単なパターンの組み合わせで絞り込めるので、慣れれば使いやすそうです。
文脈を意識した検索や置換が必要なときに候補のひとつとして覚えておくと良さそうです。