# Terminal rails g controller heavy_tasks rails g job heavy_tasks rails g job small_task rails g stimulus progress-bar
# config/routes.rb sources :heavy_tasks, solely: :create
# app/controllers/heavy_tasks_controller.rb
class HeavyTasksController < ApplicationController
before_action :authenticate_user!
def create
HeavyTaskJob.perform_later(current_user.id)
finish
finish
# app/jobs/heavy_task_job.rb
class HeavyTaskJob < ApplicationJob
queue_as :default
before_perform :broadcast_initial_update
def carry out(current_user_id)
total_count.instances do |i|
SmallTaskJob.perform_later(current_user_id, i, total_count)
finish
finish
non-public
def broadcast_initial_update
Turbo::StreamsChannel.broadcast_replace_to ["heavy_task_channel", current_user.to_gid_param].be part of(":"),
goal: "heavy_task",
partial: "heavy_tasks/progress",
locals: {
total_count: total_count
}
finish
def total_count
@total_count ||= rand(10..100)
finish
def current_user
@current_user ||= Person.discover(self.arguments.first)
finish
finish
# app/jobs/small_task_job.rb
class SmallTaskJob < ApplicationJob
queue_as :default
def carry out(current_user_id, i, total_count)
current_user = Person.discover(current_user_id)
sleep rand
# Turbo::StreamsChannel.broadcast_replace_to ["heavy_task_channel", current_user.to_gid_param].be part of(":"),
# goal: "heavy_task",
# partial: "heavy_tasks/progress",
# locals: {
# progress: (i + 1) * 100 / total_count
# }
Turbo::StreamsChannel.broadcast_action_to ["heavy_task_channel", current_user.to_gid_param].be part of(":"),
motion: "append",
goal: "heavy_task",
content material: "<div></div>"
finish
finish
# app/views/welcome/index.html.erb <%= render partial: "heavy_tasks/button" %>
# app/views/heavy_tasks/_button.html.erb <%= turbo_stream_from ["heavy_task_channel", current_user] if user_signed_in? %> <div id="heavy_task"> <%= button_to "Carry out Heavy Job", heavy_tasks_path, class: "btn btn-primary" %> </div>
# app/views/heavy_tasks/_progress.html.erb
<div id="heavy_task"
data-controller="progress-bar"
data-progress-bar-total-jobs-value="<%= total_count %>">
<div class="progress">
<div class="progress-bar progress-bar-striped energetic"
position="progressbar"
data-progress-bar-target="progress">
</div>
</div>
# app/javascript/controllers/progress_bar_controller.js
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="progress-bar"
export default class extends Controller {
static values = {
totalJobs: 0,
completedJobs: 0
}
static targets = ["progress"]
join() {
this.observer = new MutationObserver((mutationsList, _observer) => {
for (let mutation of mutationsList) {
if (mutation.sort === 'childList') {
this.increment()
}
}
})
this.observer.observe(this.ingredient, { childList: true })
}
increment() {
this.completedJobsValue++
this.updateProgress()
}
updateProgress() {
let progress = (this.completedJobsValue / this.totalJobsValue) * 100
this.progressTarget.fashion.width = `${progress}%`
}
}

