Turbo Streams and ActionCable
turbo-rails Gem Helpers
turbo-rails gem, which we added when we installed Hotwire and Turbo, provides a set of helpers for managing ActionCable connections. Those helpers allow Rails to automatically send Turbo Stream HTML over the ActionCable connections and then enable the Turbo Stream actions to be executed on the client when the message is received.
Very broadly, these helpers allow us to do three things:
- Connect a view to an ActionCable channel with a helper method in the view.
- Broadcast a Turbo Stream to an ActionCable channel as part of the response to a controller request.
- Broadcast a Turbo Stream to an ActionCable channel automatically as a callback when an ActiveRecord is modified.
The helpers provided by the
turbo-rails gem used for communicating via ActionCable differ in how much default behavior they assume and how much we can specify. At one end, we can simply send arbitrary content over an ActionCable channel managed by Turbo Streams. We can also send content and
turbo-rails can wrap it in a Turbo Stream HTML structure. We can also take advantage of the defaults and have the default partial for our model sent to a stream with a default name. Which option we choose depends on how closely our needs can be modeled by Rails conventions and also how comfortable we are defining channel behavior in our ActiveRecord model.
Preparing our views
ActionCable is particularly useful because it allows us to keep multiple browser sessions in sync, whether that means different devices for the same user, or multiple users looking at the same information. In this section, we’ll make it so that declaring a concert a favorite in one browser instantly updates any other browsers that may be open for that user.
Before we start, we can do some refactoring of our views to make it much easier to incorporate the
turbo-rails ActionCable helpers.
To make sense of the problem, it will help to back up and look at how our data flow is changing.
In our code, when we click the “Make Favorite” button to turn a concert into a favorite, we send a form request to our server, and it sends back a response made up of Turbo Stream flavored-HTML, which Turbo on the client-side uses to update the DOM.
In the version we are about to write, we still click the “Make Favorite” button and still get an HTML request, but after the new
Favorite object is created on the server-side, the server queues up and sends a Turbo Stream message back to the client via ActionCable. Turbo again updates the DOM based on the Turbo stream.
We’re sending the message using
turbo-railscommands that attach to the model, but the general problem still holds even if we send the message via ActionCable from the controller or some extra service object.
What’s important to understand about the ActionCable response is that it happens outside the regular Rails request/response cycle. Because of that, the ActionCable broadcast does not have access to global variables or session variables based on the request because it no longer takes place inside the session. For our purposes, many view partials we’ve been using to display the schedule depending on the
current_user global variable to determine whether the concert is a favorite and whether to display the “Edit” button.
We need to change the code so that any partials that will be called by ActionCable have the user passed to them rather than calling
current_user inside the partial.
The concert display part at
current_user four times. Replacing those usages with users is easy enough, but we also need to go after calls to that view. The
ConcertController especially needs to change its references: