class Imports::RunJob < ApplicationJob
queue_as :default
def perform(import_id)
import = Import.find(import_id)
import.update!(status: 'running')
broadcast(import)
import.steps.each_with_index do |step, idx|
step.call
import.update!(progress: ((idx + 1).to_f / import.steps.size * 100).round)
broadcast(import)
end
import.update!(status: 'done')
broadcast(import)
end
private
def broadcast(import)
import.broadcast_replace_to(
[import.owner, :imports],
target: dom_id(import),
partial: 'imports/import',
locals: { import: import }
)
end
end
For long operations (imports, conversions), run work in a job and stream progress updates. The job emits broadcast_update_to so the UI updates live without polling.