concord-consortium/rigse

View on GitHub
rails/lib/tasks/validate.rake

Summary

Maintainability
Test Coverage

# touch all model classes, so they show up in ActiveRecord::Base.descendants
def load_all_models()
  Dir.chdir("#{Rails.root}/app/models") do
    Dir.glob(File.join('**', '*.rb')) do |filename|
      begin
        filename.sub(/\.rb$/,'').classify.constantize
      rescue Exception
        $stderr.puts "Failed to load #{filename}: #{filename.sub(/\.rb$/,'').classify}"
      end
    end
  end
end


def for_each_model_instance(find_conditions = {}, &block)

  skip = [Embeddable::Embeddable, ActiveRecord::SessionStore::Session]
  ar_models = ActiveRecord::Base.descendants.delete_if {|m| skip.include?(m) }.sort_by{|m| m.name }
  find_conditions.merge!({:batch_size => 5})
  ar_models.each do |model|
    begin
      print "Processing #{model.count} #{model.name.pluralize}..."
      count = 0
      model.where(find_conditions).find_each do |instance|
        print("\n%6d: " % count) if count % 2000 == 0
        res = yield(model, instance)
        count += 1
        print "." if count % 50 == 0
      end
      puts " done."
    rescue Exception => ex
     $stderr.puts "Failed to process model: #{model.name}\n#{ex}\n"
    end
  end
end

def all_active_record_classes
  model_classes = ActiveRecord::Base.connection.tables.map{|table_name|
    if clazz = table_name.classify.safe_constantize
      clazz
    else
      table_name_parts = table_name.split('_')
      module_name = []
      module_name << table_name_parts.shift

      if clazz = "#{module_name.join('_').classify}::#{table_name_parts.join('_').classify}".safe_constantize
        clazz
      else
        module_name << table_name_parts.shift
        "#{module_name.join('_').classify}::#{table_name_parts.join('_').classify}".safe_constantize
      end
    end
  }.compact
end

namespace :app do
  namespace :validate do
    desc "List model instances"
    task :list_model_class_names => :environment do
      all_active_record_classes.each{|clazz| puts clazz.name}
    end

    task :count_invalid_instances => :environment do
      count = 0
      all_active_record_classes.each{|clazz|
        print clazz.name
        if [Portal::Nces06School, Portal::School].include? clazz
          puts "skipped"
          next
        end
        class_count = 0
        model_count = 0
        clazz.find_each do |model|
          begin
            if !model.valid?
              count += 1
              class_count += 1
            end
          rescue
            count += 1
            class_count += 1
          end
          model_count += 1
          print '.' if (model_count % 1000 == 0)
        end
        puts class_count
      }
      puts "total: #{count}"
    end

    desc "Process all of the model objects and find timestamps that have nil values"
    task :fix_nil_timestamps => :environment do
      (puts "You must run this rake task in production mode!" and return) unless Rails.env.production?
      load_all_models

      for_each_model_instance('created_at IS NULL OR updated_at IS NULL') do |klass, instance|
        instance.updated_at = Time.now if instance.respond_to?(:updated_at) && instance.updated_at.nil?
        instance.created_at = instance.updated_at if instance.respond_to?(:created_at) && instance.created_at.nil?
        instance.save
      end
    end

    desc "Process all of the model objects and find associations that have nil values"
    task :list_nil_associations => :environment do
      (puts "You must run this rake task in production mode!" and return) unless Rails.env.production?
      load_all_models

      OK_TO_BE_NIL = [
        [User, :portal_teacher],
        [User, :portal_student],
        [Activity, :original]
      ]
      results = {}
      assocs_cache = {}

      begin
        for_each_model_instance do |model, instance|
          results[model] ||= {}
          assocs = assocs_cache[model] ||= model.reflect_on_all_associations

          assocs.each do |assoc|
            next if OK_TO_BE_NIL.include?([model, assoc.name])
            value = instance.send(assoc.name)
            has_nil = (assoc.collection? && (value.include?(nil) || value.empty?)) || (!assoc.collection? && value.nil?)
            if has_nil
              results[model][assoc.name] ||= []
              results[model][assoc.name] << instance
            end
          end
        end
      rescue Exception => e
        $stderr.puts "Encountered exception #{e}\n#{e.backtrace.join("\n")}"
      ensure
        $stderr.puts "\n"
        results.each do |model, assocs|
          assocs.each do |assoc, instances|
            $stderr.puts "#{model.name}\##{assoc}: #{instances.map{|i| i.id}.inspect}\n"
          end
        end
      end
    end
  end
end