Broadcasting with Turbo Streams—WebSockets made simple.
14 June 2022
I love Ruby on Rails. I love the way it takes a set of assumptions about how web applications should be made and uses those to make developing web applications easier and more joyful.
One aspect of Turbo, and more specifically Turbo Streams, I only recently discovered is Broadcasts. In this short post I just want to briefly highlight how powerful they can be. There’s not a huge amount of documentation out there currently. One of the best places to learn more is the Broadcastable concern source code itself.
The problem to solve
I’m currently working on an application for tabletop roleplayers. It has a page called combat, with a list of zones. When a player clicks on a zone, their character should be moved there and—key to this demonstration—all the other players should have the data update on their view too.
On the combat page we have some basic HTML:
Obviously the HTML is being generated dynamically, with the integers being the IDs of the zone or character. The link, which I have omitted for brevity, makes a PATCH request to the zone, which updates a given characters zone. So far, so standard, but this is where the magic happens.
First, we need to subscribe to updates on a given object. This does
some magic in the background created appropriately named channels in
Action Cable (Rails’ implementation of WebSockets) which we can then
broadcast updates over. In this example, we want to subscribe to
combat, as the zones are all attributes of this.
Now, we add a small amount of code to the
Character class so that
whenever it is updated, it uses ActionCable (i.e. a WebSocket
connection) to broadcast the change and update everyone viewing the page.
And that, unbelievably, is all there is to it. We now have a page on which players can move their character between zones, and any other players can see in real-time which zone each character is in.
A quick explanation of the code above:
after_commitis our callback firing the update whenever any changes are commited on the character object. There are a lot of reasons callbacks are often a bad idea but I think this is likely an exception to that general rule. If you still don’t like them however, there’s no reason not to call your broadcast manually from the controller.
- The first argument to both
broadcast_*methods is the object to which we subscribed with
- The second argument is the DOM ID of the item on the page to update.
- The “later” in the name of the second method is recommended to speed up execution with any update except a removal.
Obviously this approach limits you to some basic CRUD-based functionality, but that is often all or most of what you need. Even if you may need to grow beyond that in the future, there’s no reason not to start here and then add more advanced Action Cable functionality as needed, similar to the natural flow from Turbo Frames to Streams to Stimulus.
To see a full list of the methods available, see the documentation or the source linked above.
Have at it!