WEBVTT

00:00:00.000 --> 00:00:00.500 align:middle line:90%


00:00:00.500 --> 00:00:03.570 align:middle line:84%
Welcome to Hotwire, an umbrella
for trio frameworks that

00:00:03.570 --> 00:00:06.780 align:middle line:84%
implement the HTML-over-the-wire
approach to building modern web

00:00:06.780 --> 00:00:07.920 align:middle line:90%
applications.

00:00:07.920 --> 00:00:10.020 align:middle line:84%
At its heart is
Turbo, which gives you

00:00:10.020 --> 00:00:11.490 align:middle line:84%
techniques for
bringing the speed

00:00:11.490 --> 00:00:13.860 align:middle line:84%
of a single-page
application without writing

00:00:13.860 --> 00:00:15.420 align:middle line:90%
a lick of JavaScript.

00:00:15.420 --> 00:00:17.190 align:middle line:84%
This screencast
will focus primarily

00:00:17.190 --> 00:00:19.020 align:middle line:90%
on showing how Turbo works.

00:00:19.020 --> 00:00:20.580 align:middle line:84%
But most applications
will eventually

00:00:20.580 --> 00:00:22.200 align:middle line:84%
need a few sprinkles
of JavaScript

00:00:22.200 --> 00:00:24.030 align:middle line:90%
to get the fidelity they need.

00:00:24.030 --> 00:00:26.050 align:middle line:84%
And for that purpose,
we have Stimulus.

00:00:26.050 --> 00:00:28.380 align:middle line:84%
It's a modest JavaScript
framework for the HTML

00:00:28.380 --> 00:00:29.670 align:middle line:90%
you already have.

00:00:29.670 --> 00:00:32.640 align:middle line:84%
I'll briefly show how this
works to supplement the Turbo

00:00:32.640 --> 00:00:34.020 align:middle line:90%
demonstration.

00:00:34.020 --> 00:00:37.320 align:middle line:84%
Finally, we have Strada, which
provides bridge tooling for how

00:00:37.320 --> 00:00:41.100 align:middle line:84%
the web and native parts of a
mobile hybrid application built

00:00:41.100 --> 00:00:43.630 align:middle line:90%
with Hotwire talk to each other.

00:00:43.630 --> 00:00:45.850 align:middle line:84%
We're still working on
polishing Strada for release,

00:00:45.850 --> 00:00:48.000 align:middle line:84%
so this screencast
will not cover it.

00:00:48.000 --> 00:00:50.310 align:middle line:84%
Hotwire is what we've
used to implement

00:00:50.310 --> 00:00:53.850 align:middle line:84%
all of the front-end elements of
hey.com, the new email service

00:00:53.850 --> 00:00:56.420 align:middle line:90%
by Basecamp.

00:00:56.420 --> 00:00:58.670 align:middle line:84%
We're going to build a
chat application using

00:00:58.670 --> 00:01:01.160 align:middle line:84%
vanilla Rails plus the
Hotwire Rails integration

00:01:01.160 --> 00:01:04.670 align:middle line:84%
gem, which includes the full
setup for Turbo and Stimulus.

00:01:04.670 --> 00:01:06.440 align:middle line:84%
The Hotwire install
command will turn

00:01:06.440 --> 00:01:09.560 align:middle line:84%
on Redis backing for handling
WebSockets with Action Cable

00:01:09.560 --> 00:01:11.600 align:middle line:84%
at the import map
needed for autoloading

00:01:11.600 --> 00:01:14.180 align:middle line:84%
Stimulus controllers
and a few other tweaks.

00:01:14.180 --> 00:01:17.270 align:middle line:84%
The application will use two
models, Room and Message.

00:01:17.270 --> 00:01:19.190 align:middle line:90%
One Room has many Messages.

00:01:19.190 --> 00:01:21.380 align:middle line:84%
We'll use a full scaffold
for the Room model

00:01:21.380 --> 00:01:23.330 align:middle line:84%
to give us the basic
editing interface,

00:01:23.330 --> 00:01:25.790 align:middle line:84%
but only use the model
generator for Message

00:01:25.790 --> 00:01:27.540 align:middle line:90%
since it needs much less.

00:01:27.540 --> 00:01:29.540 align:middle line:84%
We're going to connect
Room and Message together

00:01:29.540 --> 00:01:31.670 align:middle line:84%
without using any
of Hotwire to start.

00:01:31.670 --> 00:01:33.500 align:middle line:84%
This will give us
a foundation flow

00:01:33.500 --> 00:01:36.620 align:middle line:84%
for an admittedly cumbersome
chat application which

00:01:36.620 --> 00:01:39.380 align:middle line:84%
we can then use to level
up with Hotwire techniques

00:01:39.380 --> 00:01:40.850 align:middle line:90%
one at a time.

00:01:40.850 --> 00:01:43.520 align:middle line:84%
For Messages, we'll
have just two actions.

00:01:43.520 --> 00:01:46.310 align:middle line:84%
New to render the form
to create a message,

00:01:46.310 --> 00:01:48.860 align:middle line:84%
and Create to handle
the form submission.

00:01:48.860 --> 00:01:53.030 align:middle line:84%
Note that we are also adding a
partial template to encapsulate

00:01:53.030 --> 00:01:54.810 align:middle line:90%
rendering of the Message.

00:01:54.810 --> 00:01:56.540 align:middle line:84%
This partial is
then rendered when

00:01:56.540 --> 00:01:59.060 align:middle line:84%
showing the Room relying
on the conventional naming

00:01:59.060 --> 00:02:00.770 align:middle line:90%
to tie them together.

00:02:00.770 --> 00:02:03.740 align:middle line:84%
This is all just standard,
one-on-one rail stuff,

00:02:03.740 --> 00:02:07.420 align:middle line:84%
but let's start the
server and try it out.

00:02:07.420 --> 00:02:08.620 align:middle line:90%
Hello, Rails.

00:02:08.620 --> 00:02:11.080 align:middle line:84%
Here's our interface
to create a new Room.

00:02:11.080 --> 00:02:14.680 align:middle line:84%
Then we create a couple of
new Messages for that Room.

00:02:14.680 --> 00:02:16.720 align:middle line:84%
Yes, technically a
chat system, but not

00:02:16.720 --> 00:02:19.030 align:middle line:84%
exactly a very dynamic
or appealing one.

00:02:19.030 --> 00:02:22.660 align:middle line:84%
So let's introduce our
first Turbo feature, Frames.

00:02:22.660 --> 00:02:25.840 align:middle line:84%
Turbo Frames decompose pages
into independent contexts,

00:02:25.840 --> 00:02:28.750 align:middle line:84%
which can be lazy-loaded
and scope interaction.

00:02:28.750 --> 00:02:30.760 align:middle line:84%
So when you follow a
link or submit a form,

00:02:30.760 --> 00:02:32.830 align:middle line:84%
only the content of
the Frame changes

00:02:32.830 --> 00:02:34.880 align:middle line:90%
rather than the entire page.

00:02:34.880 --> 00:02:37.300 align:middle line:84%
This allows you to keep the
state of the rest of the page

00:02:37.300 --> 00:02:40.690 align:middle line:84%
from changing, making the
app feel more responsive.

00:02:40.690 --> 00:02:42.910 align:middle line:84%
To be able to easily
see how the Frames work,

00:02:42.910 --> 00:02:45.400 align:middle line:84%
we'll call them out
with a blue border.

00:02:45.400 --> 00:02:47.620 align:middle line:84%
Now let's wrap the Room
name and the ability

00:02:47.620 --> 00:02:49.090 align:middle line:90%
to edit it inside a Frame.

00:02:49.090 --> 00:02:52.270 align:middle line:84%
The Turbo Frame tag goes around
both the initial display,

00:02:52.270 --> 00:02:55.240 align:middle line:84%
including the Edit link, and
the part of the Edit page

00:02:55.240 --> 00:02:58.670 align:middle line:84%
we want to appear
within the Frame.

00:02:58.670 --> 00:03:00.510 align:middle line:84%
We see our Frame
wrapped in blue.

00:03:00.510 --> 00:03:03.650 align:middle line:84%
And when clicking the Edit link,
the form from the Edit screen

00:03:03.650 --> 00:03:04.940 align:middle line:90%
is presented within.

00:03:04.940 --> 00:03:06.950 align:middle line:84%
And upon submission,
it's replaced again

00:03:06.950 --> 00:03:08.180 align:middle line:90%
with just a display.

00:03:08.180 --> 00:03:11.310 align:middle line:84%
If we go straight to the
full page editing screen,

00:03:11.310 --> 00:03:14.360 align:middle line:84%
we can see it has both a
header and navigation links,

00:03:14.360 --> 00:03:17.000 align:middle line:84%
parts we were emitting
from the Frame.

00:03:17.000 --> 00:03:19.760 align:middle line:84%
Note that if we try to click
a link within the Frame that

00:03:19.760 --> 00:03:21.800 align:middle line:84%
goes somewhere without
a matching Frame,

00:03:21.800 --> 00:03:23.310 align:middle line:90%
nothing happens.

00:03:23.310 --> 00:03:26.630 align:middle line:84%
We can solve this by adding
a Data Turbo Frame attribute

00:03:26.630 --> 00:03:29.090 align:middle line:84%
that points to underscore top
to break out of the Frame,

00:03:29.090 --> 00:03:31.490 align:middle line:84%
just like traditional
HTML frames.

00:03:31.490 --> 00:03:33.360 align:middle line:84%
Now the back link
works and the Frame

00:03:33.360 --> 00:03:36.420 align:middle line:90%
scopes the Edit Display loop.

00:03:36.420 --> 00:03:38.250 align:middle line:84%
Then let's add the
New Message link

00:03:38.250 --> 00:03:40.590 align:middle line:84%
into an inline but
lazy-loaded Turbo Frame

00:03:40.590 --> 00:03:42.180 align:middle line:84%
tag that also,
just for starters,

00:03:42.180 --> 00:03:43.710 align:middle line:90%
acts on the whole page.

00:03:43.710 --> 00:03:46.470 align:middle line:84%
This Frame will be loaded
right after the page displays,

00:03:46.470 --> 00:03:47.970 align:middle line:84%
hitting the New
Message Controller

00:03:47.970 --> 00:03:49.410 align:middle line:90%
action we made earlier.

00:03:49.410 --> 00:03:52.530 align:middle line:84%
Like with Edit, we wrap the
relevant segment in a Frame tag

00:03:52.530 --> 00:03:55.320 align:middle line:84%
with a matching ID, which
is how Turbo knows how

00:03:55.320 --> 00:03:57.270 align:middle line:90%
to plug out the right Frame.

00:03:57.270 --> 00:03:58.980 align:middle line:84%
You can now see
two requests when

00:03:58.980 --> 00:04:00.900 align:middle line:84%
we load the room--
one for the page, one

00:04:00.900 --> 00:04:02.460 align:middle line:90%
for the lazy-loader frame.

00:04:02.460 --> 00:04:03.780 align:middle line:90%
Let's try to add a message.

00:04:03.780 --> 00:04:05.100 align:middle line:90%
It works!

00:04:05.100 --> 00:04:07.950 align:middle line:84%
But this only demonstrates
that the Frame was lazy-loaded.

00:04:07.950 --> 00:04:09.750 align:middle line:84%
Right now, we're
resetting the whole page

00:04:09.750 --> 00:04:11.910 align:middle line:84%
upon submission of
the New Message form.

00:04:11.910 --> 00:04:13.680 align:middle line:84%
Whereas with the
Room Name Frame,

00:04:13.680 --> 00:04:15.660 align:middle line:84%
you can edit and
submit without changing

00:04:15.660 --> 00:04:19.410 align:middle line:84%
the rest of the page state,
a real independent context.

00:04:19.410 --> 00:04:21.540 align:middle line:84%
You can see how the
Frame replacement happens

00:04:21.540 --> 00:04:23.940 align:middle line:84%
by inspecting the
response to edit.

00:04:23.940 --> 00:04:26.400 align:middle line:84%
Rails knows when a request
is coming from a Frame,

00:04:26.400 --> 00:04:28.380 align:middle line:84%
so it won't render
the layout, but that's

00:04:28.380 --> 00:04:30.330 align:middle line:84%
just a nice-to-have
have optimization.

00:04:30.330 --> 00:04:32.730 align:middle line:84%
Turbo will plug out
just the matching Frame,

00:04:32.730 --> 00:04:35.670 align:middle line:84%
regardless of whether the
response is optimized or not.

00:04:35.670 --> 00:04:39.400 align:middle line:84%
As you can see here, the
header and links are ignored.

00:04:39.400 --> 00:04:41.290 align:middle line:90%
Now let's turn to Turbo Streams.

00:04:41.290 --> 00:04:44.650 align:middle line:84%
They deliver page changes
over WebSocket or in response

00:04:44.650 --> 00:04:47.980 align:middle line:84%
to form submissions using
just HTML and a set of CRUD

00:04:47.980 --> 00:04:49.650 align:middle line:90%
like action tags.

00:04:49.650 --> 00:04:51.600 align:middle line:84%
The tags let you
append or prepend

00:04:51.600 --> 00:04:55.590 align:middle line:84%
to replace and remove any target
dom element from the existing

00:04:55.590 --> 00:04:56.550 align:middle line:90%
page.

00:04:56.550 --> 00:04:58.830 align:middle line:84%
They're strictly limited
to dom changes, though.

00:04:58.830 --> 00:05:01.020 align:middle line:90%
No direct JavaScript invocation.

00:05:01.020 --> 00:05:02.880 align:middle line:84%
If you need more
than dom change,

00:05:02.880 --> 00:05:04.635 align:middle line:90%
connect a Stimulus controller.

00:05:04.635 --> 00:05:07.650 align:middle line:84%
We will add a Turbo stream
response to the message

00:05:07.650 --> 00:05:11.100 align:middle line:84%
creation action such that we can
add the new Message to the Room

00:05:11.100 --> 00:05:13.350 align:middle line:84%
page without replacing
the whole page.

00:05:13.350 --> 00:05:15.300 align:middle line:84%
This template invokes
the Append action

00:05:15.300 --> 00:05:17.610 align:middle line:84%
with the dom ID of
the target container,

00:05:17.610 --> 00:05:20.460 align:middle line:84%
and either a full set of partial
rendering options or just

00:05:20.460 --> 00:05:22.950 align:middle line:84%
a record we wish to render
which conforms to the naming

00:05:22.950 --> 00:05:24.930 align:middle line:84%
conventions for
matching to a partial.

00:05:24.930 --> 00:05:26.760 align:middle line:84%
Now we can add
Messages to the page

00:05:26.760 --> 00:05:28.470 align:middle line:90%
without resetting it completely.

00:05:28.470 --> 00:05:30.840 align:middle line:84%
The Edit Name form can stay
open while we're doing this,

00:05:30.840 --> 00:05:34.650 align:middle line:84%
because new Messages are added
directly to the Messages div.

00:05:34.650 --> 00:05:37.470 align:middle line:84%
The Turbo Stream HTML is
rendered directly in response

00:05:37.470 --> 00:05:40.320 align:middle line:84%
to the form submission, and
Turbo knows from the MIME type

00:05:40.320 --> 00:05:42.240 align:middle line:90%
to process it automatically.

00:05:42.240 --> 00:05:44.800 align:middle line:84%
But notice the input
field isn't cleared.

00:05:44.800 --> 00:05:47.400 align:middle line:84%
We can fix that by adding
a Stimulus controller.

00:05:47.400 --> 00:05:49.140 align:middle line:84%
The new Rail
Stimulus integration

00:05:49.140 --> 00:05:51.780 align:middle line:84%
gem ships with an autoloader
for you controllers.

00:05:51.780 --> 00:05:55.620 align:middle line:84%
This is done with an import map
supported by es-module-shim,

00:05:55.620 --> 00:05:57.750 align:middle line:84%
and the unprocessed
ES6 controller

00:05:57.750 --> 00:06:01.298 align:middle line:84%
code that's loaded by the
browser directly via ESM.

00:06:01.298 --> 00:06:03.840 align:middle line:84%
You can of course continue to
use Stimulus with your existing

00:06:03.840 --> 00:06:05.850 align:middle line:84%
JavaScript bundler
and transpiler,

00:06:05.850 --> 00:06:07.890 align:middle line:84%
but this gives you a
taste of how far we're

00:06:07.890 --> 00:06:10.790 align:middle line:84%
able to get with native
browser controls now.

00:06:10.790 --> 00:06:12.540 align:middle line:84%
The Stimulus controller
we're going to add

00:06:12.540 --> 00:06:14.670 align:middle line:84%
will be a dead simple
way to reset the form

00:06:14.670 --> 00:06:16.260 align:middle line:90%
after creating a new Message.

00:06:16.260 --> 00:06:18.270 align:middle line:84%
It has just one
method, Reset, which

00:06:18.270 --> 00:06:21.720 align:middle line:84%
we will call when Turbo is done
submitting the form via Fetch.

00:06:21.720 --> 00:06:24.210 align:middle line:84%
The only novel part here
compared to existing Stimulus

00:06:24.210 --> 00:06:26.940 align:middle line:84%
is how we'll audit load
the controller at runtime

00:06:26.940 --> 00:06:30.150 align:middle line:84%
using ESM when the data
controller attribute is spotted

00:06:30.150 --> 00:06:31.590 align:middle line:90%
in the dom.

00:06:31.590 --> 00:06:34.050 align:middle line:84%
Reload to pick up the
new Stimulus controller.

00:06:34.050 --> 00:06:35.700 align:middle line:90%
Then let's add another Message.

00:06:35.700 --> 00:06:36.510 align:middle line:90%
And voila!

00:06:36.510 --> 00:06:39.665 align:middle line:84%
The form is reset and the
Message added dynamically.

00:06:39.665 --> 00:06:41.790 align:middle line:84%
But how interesting is a
chat app where you're just

00:06:41.790 --> 00:06:42.940 align:middle line:90%
talking to yourself?

00:06:42.940 --> 00:06:45.360 align:middle line:84%
Let's start a conversation
with another window.

00:06:45.360 --> 00:06:47.550 align:middle line:84%
You'll see that new
Messages are only added live

00:06:47.550 --> 00:06:49.110 align:middle line:90%
to the originator's window.

00:06:49.110 --> 00:06:52.200 align:middle line:84%
On the other side, we have to
reload to see what's been said.

00:06:52.200 --> 00:06:53.130 align:middle line:90%
Let's fix that.

00:06:53.130 --> 00:06:55.650 align:middle line:84%
The first thing we'll do
is establish a WebSocket

00:06:55.650 --> 00:06:57.360 align:middle line:84%
connection to the
stream identified

00:06:57.360 --> 00:06:58.620 align:middle line:90%
by the Room we're in.

00:06:58.620 --> 00:07:00.900 align:middle line:84%
This is done with a
Turbo Stream From tag

00:07:00.900 --> 00:07:04.150 align:middle line:84%
using a tamper-safe signed
identifier in the view.

00:07:04.150 --> 00:07:07.170 align:middle line:84%
You can see the connection has
been made to the Turbo Stream's

00:07:07.170 --> 00:07:09.720 align:middle line:84%
channel running over action
cable from the inclusion

00:07:09.720 --> 00:07:10.590 align:middle line:90%
of this text.

00:07:10.590 --> 00:07:12.510 align:middle line:84%
Let's send new
Messages to this Stream

00:07:12.510 --> 00:07:15.330 align:middle line:84%
by adding a broadcast call
to the Message creation.

00:07:15.330 --> 00:07:17.880 align:middle line:84%
This method call mirrors
what we're already

00:07:17.880 --> 00:07:20.970 align:middle line:84%
doing in the Turbo Stream
template, just over WebSocket

00:07:20.970 --> 00:07:22.330 align:middle line:90%
now.

00:07:22.330 --> 00:07:25.750 align:middle line:84%
Now we can add a new message and
see it appear in both windows.

00:07:25.750 --> 00:07:27.850 align:middle line:84%
But you'll see the
originator sees double,

00:07:27.850 --> 00:07:30.070 align:middle line:84%
because the Turbo Stream
response from the form

00:07:30.070 --> 00:07:31.840 align:middle line:90%
submission is still in place.

00:07:31.840 --> 00:07:34.030 align:middle line:84%
On the other side, we
can inspect the traffic

00:07:34.030 --> 00:07:36.280 align:middle line:84%
on the action cable
WebSocket and see

00:07:36.280 --> 00:07:38.080 align:middle line:84%
that the very same
message partial is

00:07:38.080 --> 00:07:40.630 align:middle line:84%
being used to render the
update over there, wrapped

00:07:40.630 --> 00:07:42.700 align:middle line:90%
with the same Turbo Stream tag.

00:07:42.700 --> 00:07:44.450 align:middle line:84%
There, we don't have
a form submission,

00:07:44.450 --> 00:07:47.800 align:middle line:84%
so the message is
only added once.

00:07:47.800 --> 00:07:49.750 align:middle line:84%
Let's fix the double
vision by removing

00:07:49.750 --> 00:07:51.940 align:middle line:84%
the Turbo Stream returned
in response to the form

00:07:51.940 --> 00:07:52.750 align:middle line:90%
submission.

00:07:52.750 --> 00:07:54.700 align:middle line:84%
For now, we'll just
leave a note to document

00:07:54.700 --> 00:07:57.730 align:middle line:84%
the dead end this will be
without a cable connection.

00:07:57.730 --> 00:08:01.090 align:middle line:84%
And now all updates
are sent only once.

00:08:01.090 --> 00:08:03.670 align:middle line:84%
We can also Turbo Stream
deleting messages.

00:08:03.670 --> 00:08:05.440 align:middle line:84%
We'll add a similar
model call back

00:08:05.440 --> 00:08:07.690 align:middle line:84%
and destroy the triggers
that remove broadcasts

00:08:07.690 --> 00:08:09.310 align:middle line:90%
sent to the same Stream.

00:08:09.310 --> 00:08:11.200 align:middle line:84%
But instead of
adding in a UI, let's

00:08:11.200 --> 00:08:13.480 align:middle line:84%
try to invoke this
flow from the console.

00:08:13.480 --> 00:08:15.370 align:middle line:84%
Here, you can see the
destroy carried out,

00:08:15.370 --> 00:08:18.640 align:middle line:84%
didn't spot the broadcast sent
using the Turbo Stream Remove

00:08:18.640 --> 00:08:19.750 align:middle line:90%
Action tag.

00:08:19.750 --> 00:08:20.650 align:middle line:90%
I call it again.

00:08:20.650 --> 00:08:23.680 align:middle line:84%
Another line disappears
from both browser windows.

00:08:23.680 --> 00:08:25.147 align:middle line:84%
You can also create
a new message

00:08:25.147 --> 00:08:26.230 align:middle line:90%
straight from the console.

00:08:26.230 --> 00:08:29.020 align:middle line:84%
And here, you'll see the same
Turbo Stream Append action

00:08:29.020 --> 00:08:32.929 align:middle line:84%
with the same partial template,
as with all the other examples.

00:08:32.929 --> 00:08:34.870 align:middle line:84%
Finally, we can add
a replace to happen

00:08:34.870 --> 00:08:36.309 align:middle line:90%
with the message is updated.

00:08:36.309 --> 00:08:38.710 align:middle line:84%
This follows the same flow
and completes the life

00:08:38.710 --> 00:08:40.240 align:middle line:84%
cycle of callbacks
you'd normally

00:08:40.240 --> 00:08:42.320 align:middle line:90%
respond to with Stream updates.

00:08:42.320 --> 00:08:43.900 align:middle line:90%
Let's invoke from the console.

00:08:43.900 --> 00:08:44.500 align:middle line:90%
Whoops!

00:08:44.500 --> 00:08:46.570 align:middle line:84%
We have to reload
our console instance

00:08:46.570 --> 00:08:48.310 align:middle line:90%
to pick up the code change.

00:08:48.310 --> 00:08:49.150 align:middle line:90%
And here we go.

00:08:49.150 --> 00:08:51.770 align:middle line:84%
The last message in the
chat has been updated.

00:08:51.770 --> 00:08:53.140 align:middle line:90%
But it can be even simpler.

00:08:53.140 --> 00:08:55.960 align:middle line:84%
If you want a full menu of
basic lifecycle updates,

00:08:55.960 --> 00:08:57.760 align:middle line:84%
you can replace
these three callbacks

00:08:57.760 --> 00:09:00.580 align:middle line:84%
with a single broadcast
to declaration.

00:09:00.580 --> 00:09:04.000 align:middle line:84%
You'll notice that this setup
even uses async broadcasts.

00:09:04.000 --> 00:09:07.840 align:middle line:84%
Surrendering is done out
of band by a job queue.

00:09:07.840 --> 00:09:09.670 align:middle line:84%
Let's add this
shortened form directly

00:09:09.670 --> 00:09:11.530 align:middle line:90%
to changing the room as well.

00:09:11.530 --> 00:09:13.480 align:middle line:84%
Since the room
identifies the Stream,

00:09:13.480 --> 00:09:15.400 align:middle line:90%
we could just use broadcasts.

00:09:15.400 --> 00:09:17.800 align:middle line:84%
Then we'll extract the room
display as its own partial

00:09:17.800 --> 00:09:20.890 align:middle line:84%
so it'll match the conventions
assumed by the callback,

00:09:20.890 --> 00:09:23.230 align:middle line:84%
render it in line,
and give it a dom ID.

00:09:23.230 --> 00:09:25.120 align:middle line:84%
Now you can edit the
name in one window

00:09:25.120 --> 00:09:28.620 align:middle line:84%
and instantly see it
updated in the other.

00:09:28.620 --> 00:09:31.020 align:middle line:84%
So that's Hotwire, an
alternative approach

00:09:31.020 --> 00:09:33.360 align:middle line:84%
to building modern web
applications without using

00:09:33.360 --> 00:09:38.370 align:middle line:84%
much JavaScript by sending HTML
instead of JSON over the wire.

00:09:38.370 --> 00:09:41.280 align:middle line:84%
We get to keep all our template
rendering on the server, which

00:09:41.280 --> 00:09:43.230 align:middle line:84%
means writing more
of our application

00:09:43.230 --> 00:09:46.910 align:middle line:84%
in our favorite
programming languages.

00:09:46.910 --> 00:09:49.520 align:middle line:84%
Now let's have a quick look at
how these techniques are used

00:09:49.520 --> 00:09:51.290 align:middle line:90%
in a real-life application.

00:09:51.290 --> 00:09:54.260 align:middle line:84%
We at Basecamp launched our
new email service, hey.com,

00:09:54.260 --> 00:09:55.460 align:middle line:90%
this past summer.

00:09:55.460 --> 00:09:58.370 align:middle line:84%
And within 40 kilobytes
of compressed JavaScript,

00:09:58.370 --> 00:10:01.730 align:middle line:84%
we're able to deliver a full
featured modern and successful

00:10:01.730 --> 00:10:04.910 align:middle line:84%
email app, complete with
native applications across all

00:10:04.910 --> 00:10:08.360 align:middle line:84%
the major platforms, powered
by the same majestic monolith

00:10:08.360 --> 00:10:11.500 align:middle line:90%
running on Hotwire.

00:10:11.500 --> 00:10:13.690 align:middle line:84%
HEY uses lazy-loaded
Turbo Frames

00:10:13.690 --> 00:10:16.510 align:middle line:84%
to fetch the Reply Later
and Set Aside trays

00:10:16.510 --> 00:10:17.860 align:middle line:90%
at the bottom of the inbox.

00:10:17.860 --> 00:10:21.070 align:middle line:84%
These trays change far less
frequently than the inbox

00:10:21.070 --> 00:10:23.290 align:middle line:84%
itself, so they're
excellent candidates

00:10:23.290 --> 00:10:25.840 align:middle line:84%
to run on a different
caching schedule.

00:10:25.840 --> 00:10:28.270 align:middle line:84%
They're loaded as soon as
the initial page is loaded,

00:10:28.270 --> 00:10:31.030 align:middle line:84%
but will 302 when
already cached when

00:10:31.030 --> 00:10:32.920 align:middle line:90%
you hit them a second time.

00:10:32.920 --> 00:10:35.860 align:middle line:84%
We also use lazy-loaded
Turbo Frames for menus,

00:10:35.860 --> 00:10:37.960 align:middle line:84%
but these are not loaded
until you access them

00:10:37.960 --> 00:10:39.338 align:middle line:90%
for the first time.

00:10:39.338 --> 00:10:41.380 align:middle line:84%
Once loaded, you'll be
hitting that browser cache

00:10:41.380 --> 00:10:43.180 align:middle line:90%
on subsequent visits.

00:10:43.180 --> 00:10:46.390 align:middle line:84%
This technique is used for both
the HEY menu and the ME menu,

00:10:46.390 --> 00:10:50.680 align:middle line:84%
both triggered by the
detailed summary tag pairing.

00:10:50.680 --> 00:10:54.760 align:middle line:84%
The Topic page uses even more
lazy-loaded Turbo Frames,

00:10:54.760 --> 00:10:58.810 align:middle line:84%
since the basic page is shared
amongst all users on a company

00:10:58.810 --> 00:11:01.450 align:middle line:84%
account for maximum
cache efficiency

00:11:01.450 --> 00:11:06.310 align:middle line:84%
and all per-user specialization
is therefore done with Frames.

00:11:06.310 --> 00:11:09.310 align:middle line:84%
But let's focus just on
the Toolbar Frame, which

00:11:09.310 --> 00:11:10.930 align:middle line:90%
is where you start a new reply.

00:11:10.930 --> 00:11:13.420 align:middle line:84%
When Reply Now is
pressed, that Frame

00:11:13.420 --> 00:11:16.150 align:middle line:84%
is replaced with a Frame
response of the New Message

00:11:16.150 --> 00:11:17.050 align:middle line:90%
page.

00:11:17.050 --> 00:11:19.780 align:middle line:84%
If we delete that response,
we again replace the Frame

00:11:19.780 --> 00:11:20.920 align:middle line:90%
with the toolbar.

00:11:20.920 --> 00:11:25.270 align:middle line:84%
Barely any JavaScript needed
for the whole interaction.

00:11:25.270 --> 00:11:27.740 align:middle line:84%
Now let's go back and have
a look at the Screener.

00:11:27.740 --> 00:11:30.330 align:middle line:90%


00:11:30.330 --> 00:11:32.250 align:middle line:84%
The Screener actions
are synchronized

00:11:32.250 --> 00:11:35.700 align:middle line:84%
between browser windows,
just like in our chat demo

00:11:35.700 --> 00:11:36.990 align:middle line:90%
application.

00:11:36.990 --> 00:11:39.840 align:middle line:84%
Although here, updates
needed for different pages

00:11:39.840 --> 00:11:42.130 align:middle line:90%
require unique responses.

00:11:42.130 --> 00:11:43.950 align:middle line:84%
So we simply remove
the pending email

00:11:43.950 --> 00:11:46.380 align:middle line:84%
from the Screener upon
clicking the button,

00:11:46.380 --> 00:11:48.930 align:middle line:84%
but we update both
the Screener button

00:11:48.930 --> 00:11:51.720 align:middle line:84%
and reveal the Screened
in email in the inbox

00:11:51.720 --> 00:11:53.770 align:middle line:90%
on the other screen.

00:11:53.770 --> 00:11:57.150 align:middle line:84%
You'll see we're still using
a slightly older Turbo Stream

00:11:57.150 --> 00:12:01.500 align:middle line:84%
syntax here, with a
template tag as the head,

00:12:01.500 --> 00:12:05.410 align:middle line:84%
but we'll be changing to
the latest API shortly.

00:12:05.410 --> 00:12:09.200 align:middle line:84%
Lastly, you'll see how Frames
and Streams can interact.

00:12:09.200 --> 00:12:12.130 align:middle line:84%
When you set aside an email,
we remove it from the inbox,

00:12:12.130 --> 00:12:16.720 align:middle line:84%
then add it to the Set Aside
tray in both windows at once.

00:12:16.720 --> 00:12:19.840 align:middle line:84%
The removal in the Acting
window is done via Turbo Stream

00:12:19.840 --> 00:12:22.630 align:middle line:84%
response to the action,
and then both windows

00:12:22.630 --> 00:12:26.710 align:middle line:84%
have the Set Aside email added
to the tray over WebSocket.

00:12:26.710 --> 00:12:28.600 align:middle line:84%
You can check out
all the ways we've

00:12:28.600 --> 00:12:30.820 align:middle line:84%
used Hotwire to build
HEY by signing up

00:12:30.820 --> 00:12:32.680 align:middle line:90%
for free trial account.

00:12:32.680 --> 00:12:35.230 align:middle line:84%
All the JavaScript is
available with source maps.

00:12:35.230 --> 00:12:37.960 align:middle line:84%
All the Turbo interactions
were sent in the clear,

00:12:37.960 --> 00:12:39.700 align:middle line:84%
so there's plenty to
learn from, and we

00:12:39.700 --> 00:12:43.330 align:middle line:84%
welcome you to borrow or steal
our Stimulus controllers.

00:12:43.330 --> 00:12:46.120 align:middle line:84%
I hope you've enjoyed this
quick tour of Hotwire,

00:12:46.120 --> 00:12:48.130 align:middle line:90%
and Turbo in particular.

00:12:48.130 --> 00:12:50.785 align:middle line:84%
Please help us further
develop all the frameworks

00:12:50.785 --> 00:12:52.180 align:middle line:90%
as open source.

00:12:52.180 --> 00:12:53.500 align:middle line:90%
Thanks.

00:12:53.500 --> 00:12:54.000 align:middle line:90%