How to iterate over all ActiveRecord models

Sometimes you have a maintenance script where you want to iterate over all ActiveRecord models. Rails provides this out of the box:

# script/maintenance_task.rb

# Load all models eagerly, otherwise you might only get a subset
Rails.application.eager_load!

ApplicationRecord.descendants.select(&:table_exists?).each do |model|
  # ...
end

Caution

If you iterate over individual records, please provide a progress indicator: See https://makandracards.com/makandra/625369-upload-run-scripts-production

Caution

At makandra, we use our gem active_type Show archive.org snapshot for model inheritance. Depending on your use case, you might want to skip those models.

Full example

Rails.application.eager_load!

# Inlined here, but if you need this in multiple scripts, prefer the approach from the linked card above
module Enumerable
  def with_progress(title = 'Progress')
    total = count.to_f
    each_with_index do |item, index|
      progress = (index + 1) / total * 100
      printf("#{title}: %.2f%%\r", progress)
      yield item
    end
    puts "#{title} is done."
  end
end

ApplicationRecord.descendants.select(&:table_exists?).each do |model|
  # I wanted to iterate over all models with a CarrierWave uploader
  attributes = model.uploaders.keys
  next if attributes.empty?

  # Skip active_type models
  if model < ActiveType::RecordExtension::Inheritance
    puts "Skipped ActiveType form model #{model}"
    puts
    next
  end

  # Perform your migration with helpful progress output
  puts "Fixing uploads for #{model}: #{attributes.to_json}"

  model.find_each.with_progress(model.to_s) do |record|
    attributes.each do |attribute|
      record.public_send(attribute).my_custom_migration_task!
    end
  end

  puts
end
Klaus Weidinger