Slide 1

Slide 1 text

Turbo Stream Live, asynchronous updates to RoR applications views from the backend

Slide 2

Slide 2 text

Krzysztof Kamil Piotrowski https://github.com/krzykamil [email protected] 2N IT https://github.com/2N-IT/ https://www.2n.pl/

Slide 3

Slide 3 text

Agenda 1. Turbo/! ○ Turbo Stream basics 2. Demo app ○ Step by step analysis 3. Summary 4. Resources - QA

Slide 4

Slide 4 text

Basics - TLDR; Turbo, Hotwire: ● Turbo is part of Hotwire ● Hotwire minimizes the need for Javascript (huray!), by delivering HTML "over the wire" ● Hotwire is used more and more in RoR environment ● Focus is to render templates on the server side, deliver them to the client without rendering the whole page, but only the needed part of it ● No need to refresh the whole page to see the changes from client actions on the page (form submissions, path changes (SPA-like)) ● TurboStream "delivers page changes over WebSocket, Server-Sent Events or in response to form submissions"

Slide 5

Slide 5 text

Basics - stream in html template This is how it looks in pure HTML (ofc. rails gives helpers to generate that, coming up in the next slide)

Slide 6

Slide 6 text

Basics - Regular, basic rails usage

Slide 7

Slide 7 text

Basics When broadcast is sent, something like this appear in the DOM for a brief moment Turbo internal JS kicks in, changes the HTML. You do not have to do anything else on the FE

Slide 8

Slide 8 text

This makes for some pretty impressive stuff ● Real time updates to pages - no reloads ● Faster development of common and uncommon FE tasks and problems ● No Javascript ● Reactive elements with no javascript ● Server-side handling of frontend problems ● No JS ● Can be used outside Rails (although, in this case has to be used through DSL in Typescript) ● Other than that, using Turbo minimizes or obliterates your need for Javascript

Slide 9

Slide 9 text

But all of this you can learn from the documentation. For now the documentation is sparse and covers just the basics. If you want a more advanced turbo usage, you are left with just the source code and turbo-rails gem for help We do not want to always have to deal with partials and put everything in the controller or add even more stuff to already big rails models There are multiple operations in most systems, that affect the data of the application, that in turn affects the view. Why not simplify and speed up the process of clicking through and refreshing pages? Basically lets introduce side effects of backend actions to the view more directly, without coupling

Slide 10

Slide 10 text

Few words about resources The gem for turbo on rails is good (IMHO), and the source code good is clean and has a lot of comments that are helpful for us to use TurboStream in a more interesting way There is still little resources so far around Turbo, and most of them are repetitive, show you the same thing that the documentation does

Slide 11

Slide 11 text

Demo app stack ● Sidekiq ● Trailblazer Cells (you could replace that with githubs ViewComponents) ● turbo-rails gem (just for the streams) ● redis (used by ActionCable that in turn is used by TurboStream) ● Blog (original I know) ● Rails 6.0.2 ● Ruby 3.0.2

Slide 12

Slide 12 text

Our demo case: ● User is displaying a index page of a blog ● Author of the blog has created a structure of the page that has 1 post exposed, then two in a next line, and then 3 in the next line ● Author of the blog wants the entire structure of what post is exposed or in what line to change from time to time, without the users reloading to see the change

Slide 13

Slide 13 text

Videos presentation link ● A big part of the page can be updated - the cell is a part of the entire view, it is not the entire view ● The layout was not rendered again, just the cell html was redone ● rake task that fires sidekiq and sidekiq itself had no knowledge of client session, yet the client view was altered ● Pretty much everything happens on server side, no JS was written here by me

Slide 14

Slide 14 text

The code - What we with the implementation 1. Minimize changes in html 2. Opt-in through cell 3. Decide how to render in cell 4. Only render if stream active

Slide 15

Slide 15 text

Minimize changes in html Default turbo approach involves adding html (or rails helpers) to a lot of partials if you use turbo the default way. This often also leads to more code in controllers (some parts of turbo require special format handling). If you add turbo to an existing rails app, you will end up adding quite a lot of HTML and controller code (still better than a lot of JS). Using view components like cells help minimize that

Slide 16

Slide 16 text

The change, from top to bottom

Slide 17

Slide 17 text

This is what fuels it

Slide 18

Slide 18 text

Opt-in through cell I wanted to use cells/githubs view components cause they are mose reusable and make the htmls smaller. Also it is hard to render a partial from a sidekiq worker. Also it makes it easy to search for all components that use turbo It is also nice to have a ruby object that determines what to do and when, with the broadcast signal received

Slide 19

Slide 19 text

How it works

Slide 20

Slide 20 text

How it works cont.

Slide 21

Slide 21 text

Decide how to render in cell From external places that can affect the view it would be nice to simply call always the same object with as simple parameters as possible and be done with it. Do not couple the view with the backend. I don't want the BE to know what cell its rendering since that would couple it to the view We want to use a general object to avoid coupling stuff like workers to cells

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

How it works cont. How it works part 1

Slide 24

Slide 24 text

How it works part 2

Slide 25

Slide 25 text

Only render if stream is active I need BE to know that someone will receive a broadcast, to not render html without a need Do not waste resources, specially given ActionCable problems with being up to date Since backend is decoupled from the user sessions and you can't access ActionCable easily from anywhere I wanted to avoid simply always broadcasting in hopes someone is listening.

Slide 26

Slide 26 text

How it works

Slide 27

Slide 27 text

Some other usages this could give us: ● Could be integrated with event sourcing (the original usage I worked with) ● Could work across microservices ● As well as gems (this implementation itself could be a gem) ● As well as rails engines ● In short - as long as you can access the redis instance and registry of cells using streams you can make it work

Slide 28

Slide 28 text

Can be used to make BE business logic affect FE for live users immediately. This can be done from any place in the code not just the controller-model It's a tool with great potential, its fast and the source code of the ruby gem, that serves as a bridge to the actual Turbo is written well Turbo Stream Uses Action Cable but is missing some tools that would make it much easier to use, for example there is no easy way to detect current live session/clients. You need to dig into redis if you configure Action Cable to use it, you check that. Its a young tool with most of the materials on the web so far only repeating the documentation Basecamp provided, not being very innovative

Slide 29

Slide 29 text

Closing notes So far the best, most comprehensive Hotwire (mostly focused on Turbo) tutorial I saw was this: https://www.hotrails.dev This presentation used a sample app, which code is public on: https://github.com/krzykamil/turbo_p rez