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 and make your tests faster and easier to maintain.