This tutorial demonstrates how simple it is to perform DOM updates from Rails background jobs with CableReady.
Intro
Ruby on Rails supports websockets out of the box via a built in library known as ActionCable. I created a library named CableReady that works with ActionCable to perform common DOM operations from background jobs without requiring you to write any custom JavaScript. And, it's very performant.
1. Create the Rails project
rails new progress_bar_demo
cd progress_bar_demo
2. Create the restful resource
First create the controller and HTML page.
bundle exec rails generate controller progress_bars
touch app/views/progress_bars/show.html.erb
<!-- app/views/progress_bars/show.html.erb -->
<h1>Progress Bar Demo</h1>
<div id="progress-bar">
<div></div>
</div>
Then update the routes file.
# config/routes.rb
Rails.application.routes.draw do
resource :progress_bar, only: [:show]
root "progress_bars#show"
end
3. Setup the styling
First create the stylesheet.
mkdir app/javascript/stylesheets
touch app/javascript/stylesheets/application.scss
// app/javascript/stylesheets/application.scss
#progress-bar {
background-color: #ccc;
border-radius: 13px;
padding: 3px;
}
#progress-bar>div {
background-color: green;
width: 0;
height: 20px;
border-radius: 10px;
}
Then update the JavaScript pack to include the stylesheet.
// app/javascript/packs/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
import "../stylesheets/application.scss" // <-- add this line
Finally, update the application layout to use the stylesheet pack.
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title>ProgressBarDemo</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<!-- line below was updated to use stylesheet_pack_tag -->
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= yield %>
</body>
</html>
4. Setup the ActionCable channel
yarn add cable_ready
bundle exec rails generate channel progress_bar
// app/javascript/channels/progress_bar_channel.js
import consumer from "./consumer"
import CableReady from 'cable_ready'
consumer.subscriptions.create("ProgressBarChannel", {
received: data => {
if (data.cableReady) CableReady.perform(data.operations)
}
})
# app/channels/progress_bar_channel.rb
class ProgressBarChannel < ApplicationCable::Channel
def subscribed
stream_from "ProgressBarChannel"
end
end
5. Setup the backend
bundle add cable_ready
bundle exec rails generate job progress_bar
When this job fires, it runs a loop that fills in the progress bar a little bit on each iteration. This is possible because CableReady allows us to send commands to the browser that update the DOM without the need to write custom Javascript.
# app/jobs/progress_bar_job.rb
class ProgressBarJob < ApplicationJob
include CableReady::Broadcaster
queue_as :default
def perform
status = 0
while status < 100
status += 10
cable_ready["ProgressBarChannel"].set_attribute(
selector: "#progress-bar>div",
name: "style",
value: "width:#{status}%"
)
cable_ready.broadcast
sleep 1 # fake some latency
end
end
end
# app/controllers/progress_bars_controller.rb
class ProgressBarsController < ApplicationController
def show
ProgressBarJob.set(wait: 1.second).perform_later
end
end
6. Run and watch the magic
bundle exec rails s
Then visit http://localhost:3000 in a browser.
Disclaimer
⚠️ This demo is tailored for the development environment. In a production setup you'd need to configure both ActionCable and ActiveJob to use Redis. You'd also want to secure the ActionCable channel.
Top comments (2)
Thank you very much @hopsoft for CableReady . I just tried the progress bar tutorial and I think it was the easiest ActionCable progress bar I ever did. I hope you will have more time in the future for more tutorials.
Added
transition: .5s
to get a smoother progress transition.As I used Tachyons CSS on my project and I had to add the transition inside the ProgressBarJob as the html content was replaced by the CableReady real-time update.
However, the transition can be added directly to the application.scss .
Update
After playing some time with CableReady, I wanted to extend the progress bar with a real-time feedback, to help the end user see the current step of the progress bar.
Note that the CableReady docs are pretty terrible. I plan to get them updated soon.