Whenever a new association is added usually we also need the opposite association to ensure things get cleaned up properly during deletion.
To never forget this and audit the current state, these two tests can help.
def all_models
models = Dir["app/models/**/*.rb"].grep_v(/\/concerns\//)
models.size.must_be :>, 20
models.each { |f| require f }
ActiveRecord::Base.descendants
end
it "explicity defines what should happen to dependencies" do
bad = all_models.flat_map do |model|
model.reflect_on_all_associations.map do |association|
next if association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
next if association.options.key?(:through)
next if association.options.key?(:dependent)
"#{model.name} #{association.name}"
end
end.compact
assert(
bad.empty?,
"These associations need a :dependent defined (most likely :destroy or nil)\n#{bad.join("\n")}"
)
end
it "links all dependencies both ways so dependencies get deleted reliably" do
bad = all_models.flat_map do |model|
model.reflect_on_all_associations.map do |association|
next if association.name == :audits
next if association.options.fetch(:inverse_of, false).nil? # disabled on purpose
next if association.inverse_of
"#{model.name} #{association.name}"
end
end.compact
assert(
bad.empty?,
<<~TEXT
These associations need an inverse association.
For example project has stages and stage has project.
If automatic connection does not work, use `:inverse_of` option on the association.
If inverse association is missing AND the inverse should not destroyed when dependency is destroyed, use `inverse_of: nil`.
#{bad.join("\n")}
TEXT
)
end