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