discourse/migrations/lib/uploader/tasks/fixer.rb
Gerhard Schlager 89f26da39d
MT: Switch to nested module style across migrations/ (#38564)
Ruby's compact module syntax (`module
Migrations::Database::Schema::DSL`) breaks lexical constant lookup —
`Module.nesting` only includes the innermost constant, so every
cross-module reference must be fully qualified. In practice this means
writing `Migrations::Database::Schema::Helpers` even when you're already
inside `Migrations::Database::Schema`.

Nested module definitions restore the full nesting chain, which brings
several practical benefits:

- **Less verbose code**: references like `Schema::Helpers`,
`Database::IntermediateDB`, or `Converters::Base::ProgressStep` work
without repeating the full path from root
- **Easier to write new code**: contributors don't need to remember
which prefixes are required — if you're inside the namespace, short
names just work
- **Fewer aliasing workarounds**: removes the need for constants like
`MappingType = Migrations::Importer::MappingType` that existed solely to
shorten references
- **Standard Ruby style**: consistent with how most Ruby projects and
gems structure their namespaces

The diff is large but mechanical — no logic changes, just module
wrapping and shortening references that the nesting now resolves.
Generated code (intermediate_db models/enums) keeps fully qualified
references like `Migrations::Database.format_*` since it must work
regardless of the configured output namespace.

- Convert 138 lib files from compact to nested module definitions
- Remove now-redundant fully qualified prefixes and aliases
- Update model and enum writers to generate nested modules with correct
indentation
- Regenerate all intermediate_db models and enums
2026-03-19 18:15:19 +01:00

88 lines
2.4 KiB
Ruby

# frozen_string_literal: true
module Migrations
module Uploader
module Tasks
class Fixer < Base
def run!
return if max_count.zero?
puts "Fixing missing uploads..."
status_thread = start_status_thread
consumer_threads = start_consumer_threads
producer_thread = start_producer_thread
producer_thread.join
work_queue.close
consumer_threads.each(&:join)
status_queue.close
status_thread.join
end
private
def max_count
@max_count ||=
uploads_db.db.query_single_splat(
"SELECT COUNT(*) FROM uploads WHERE upload IS NOT NULL",
)
end
def enqueue_jobs
uploads_db
.db
.query(
"SELECT id, upload FROM uploads WHERE upload IS NOT NULL ORDER BY rowid DESC",
) { |row| work_queue << row }
end
def instantiate_task_resource
OpenStruct.new(url: "")
end
def handle_status_update(result)
@current_count += 1
case result[:status]
when :ok
# ignore
when :error
@error_count += 1
puts " Error in #{result[:id]}"
when :missing
@missing_count += 1
puts " Missing #{result[:id]}"
uploads_db.db.execute("DELETE FROM uploads WHERE id = ?", result[:id])
Upload.delete_by(id: result[:upload_id])
end
end
def process_upload(row, fake_upload)
upload = JSON.parse(row[:upload], symbolize_names: true)
fake_upload.url = upload[:url]
path = add_multisite_prefix(discourse_store.get_path_for_upload(fake_upload))
status = file_exists?(path) ? :ok : :missing
update_status_queue(row, upload, status)
rescue StandardError => error
puts error.message
status = :error
update_status_queue(row, upload, status)
end
def update_status_queue(row, upload, status)
status_queue << { id: row[:id], upload_id: upload[:id], status: }
end
def log_status
error_count_text = error_count > 0 ? "#{error_count} errors".red : "0 errors"
print "\r%7d / %7d (%s, %s missing)" %
[current_count, max_count, error_count_text, missing_count]
end
end
end
end
end