This week, I upgraded a little demo application for my book Deployment from Scratch from Rails 6 to Rails 6.1. Since I showcase WebSockets with ActionCable and Redis, I needed to move the ActionCable CoffeeScript from Sprockets to Webpacker.
I started with dependencies. The original application could lose uglifier
as Sprockets’ JavaScript processor and coffee-rails
in favour of JavaScript. I replaced them with webpacker
gem in Gemfile
:
gem 'webpacker', '~> 5.4'
Once I generated a new Gemfile.lock
, I could run a webpacker:install
tasks that creates many files (which I won’t get into here):
$ rails webpacker:install
In case you won’t see the new Webpacker tasks, make sure to delete the Rails cache:
$ rails tmp:cache:clear
It took me a while to realize why I don’t see this Webpacker Rake task.
Once that’s done, let’s see how to move the JavaScript entry point file.
// app/assets/javascripts/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .
All these requirements should now happen in the new app/javascript
directory:
// app/javascript/packs/application.js
import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
Rails.start()
ActiveStorage.start()
After I had my new application.js
ready, I changed javascript_include_tag
to javascript_pack_tag
in views:
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
Then I updated the channels. I went from this:
// app/assets/javascripts/cable.js
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
//
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
To new channels structure with channels/index.js
and channels/consumer.js
:
// app/javascript/channels/index.js
// Load all the channels within this directory and all subdirectories.
// Channel files must be named *_channel.js.
const channels = require.context('.', true, /_channel\.js$/)
channels.keys().forEach(channels)
// app/javascript/channels/consumer.js
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
And then I rewrote my original subscription file that looked like this:
// app/assets/javascripts/cable/subscriptions/document.coffee
App.cable.subscriptions.create { channel: "DocumentChannel" },
connected: () ->
received: (data) ->
console.log("Received data.")
alert(data["title"])
To a JavaScript version using the previous consumer.js
file:
// app/javascript/channels/documents_channel.js
import consumer from "./consumer"
consumer.subscriptions.create(
{ channel: "DocumentChannel" },
{
connect() {},
received(data) {
console.log("Received data.")
alert(data["title"])
}
}
)
At this point the all the new files are in place, I just had to go and delete the old app/assets/javascript
directory:
$ rm -rf app/assets/javascripts
And remove it from the manifest (the second line):
// app/assets/config/manifest.js
//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
Although it’s a small app with only one channel, you might find this useful if you didn’t move to Webpacker yet.
Get Test Driving Rails while it's in prerelease.