Skip to content

Instantly share code, notes, and snippets.

@KieranP
Last active October 11, 2015 11:08
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save KieranP/3849777 to your computer and use it in GitHub Desktop.
Save KieranP/3849777 to your computer and use it in GitHub Desktop.
Rake task that scans all Rails models for orphaned belong_to associations (Rails 3.2 or higher)
task :orphaned_check => :environment do
Dir[Rails.root.join('app/models/*.rb').to_s].each do |filename|
klass = File.basename(filename, '.rb').camelize.constantize
next unless klass.ancestors.include?(ActiveRecord::Base)
orphanes = Hash.new
klass.reflect_on_all_associations(:belongs_to).each do |belongs_to|
assoc_name, field_name = belongs_to.name.to_s, belongs_to.foreign_key.to_s
if belongs_to.options[:polymorphic]
foreign_type_field = field_name.gsub('_id', '_type')
foreign_types = klass.unscoped.select("DISTINCT(#{foreign_type_field})")
foreign_types = foreign_types.collect { |r| r.send(foreign_type_field) }
foreign_types.sort.each do |foreign_type|
related_sql = foreign_type.constantize.unscoped.select(:id).to_sql
finder = klass.unscoped.select(:id).where("#{foreign_type_field} = '#{foreign_type}'")
finder.where("#{field_name} IS NOT NULL AND #{field_name} NOT IN (#{related_sql})").each do |orphane|
orphanes[orphane] ||= Array.new
orphanes[orphane] << [assoc_name, field_name]
end
end
else
class_name = (belongs_to.options[:class_name] || assoc_name).classify
related_sql = class_name.constantize.unscoped.select(:id).to_sql
finder = klass.unscoped.select(:id)
finder.where("#{field_name} IS NOT NULL AND #{field_name} NOT IN (#{related_sql})").each do |orphane|
orphanes[orphane] ||= Array.new
orphanes[orphane] << [assoc_name, field_name]
end
end
end
orphanes.sort_by { |record, data| record.id }.each do |record, data|
data.sort_by(&:first).each do |assoc_name, field_name|
puts "#{record.class.name}##{record.id} #{field_name} is present, but #{assoc_name} doesn't exist"
end
end
end
end
@smoyte
Copy link

smoyte commented Aug 20, 2013

this is great!

a version of this script that handles namespaced classes and allows deleting via a command line option:

https://gist.github.com/hooverlunch/6275940

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment