00:00:06.960
So, I was trying to figure out what I
00:00:09.360
would talk about um or submit for a talk
00:00:12.160
at Rails World this year. And um a theme
00:00:15.839
I've kind of been obsessed about uh with
00:00:19.600
Rails has been like what makes Rails
00:00:22.160
feel so special. So, today we're going
00:00:24.000
to talk about uh beyond the basics and
00:00:26.240
getting into some advanced Rails
00:00:27.760
techniques. Um, I'm super excited to be
00:00:31.599
here again. Uh, last time in 2023 was
00:00:35.600
super special. So, it's it's great to be
00:00:37.840
back up on stage here. Um, so let's dive
00:00:40.559
in. What makes code feel Railsy? Uh,
00:00:44.160
it's kind of, you know, a nebulous
00:00:46.320
topic. Um, but I think David's talked
00:00:48.640
about this uh several times in the past
00:00:51.039
where like Ruby allows your code to feel
00:00:54.399
on equal footing as the code inside of
00:00:56.960
Ruby itself. active support can extend
00:01:00.800
uh integer class to say you know 7 days
00:01:04.239
and all kinds of cool things like that.
00:01:06.720
Um and terminology is a really important
00:01:10.400
piece of the Rails puzzle. Um when we
00:01:13.840
look at things like active record you
00:01:16.560
say has many subscribers has one
00:01:19.360
attached featured image has rich text
00:01:21.680
description validations are really
00:01:23.680
descriptive as well. Um, and that's one
00:01:26.960
of the things that is so special about
00:01:29.119
Rails. And the Ruby language allows that
00:01:31.600
to be possible. And this applies to our
00:01:34.720
controllers, too. So, we can have before
00:01:36.560
action authenticate user, and it just
00:01:39.680
does what it sounds like it's going to
00:01:41.439
do.
00:01:42.960
But, uh, with Rails 8 last year, I
00:01:46.159
thought, um, the allow unauthenticated
00:01:48.960
access caught my attention. I thought
00:01:50.720
that was pretty interesting. It kind of
00:01:52.640
does the opposite of devise where things
00:01:54.960
are uh locked down to registered users
00:01:58.240
or signed in users by default and you
00:02:00.640
break out of it. But instead of adding a
00:02:03.119
before action that you call, it has a
00:02:06.159
class method you call instead. So it
00:02:08.319
doesn't actually blend in with all of
00:02:09.920
the other before actions in your
00:02:12.000
controller, which you might have three
00:02:13.440
or four or five of them, and you have to
00:02:15.360
read the second word to actually
00:02:17.040
understand what is going on there. So
00:02:20.160
these stand out on purpose uh a bit and
00:02:23.520
the same applies to rate limiting. Rate
00:02:26.239
limiting reads extremely well. Rate
00:02:28.720
limit uh request to this controller to
00:02:31.200
10 within 5 minutes. I love it. But what
00:02:35.040
you might not realize is you can write
00:02:36.720
those class methods in your code too.
00:02:38.959
Typically we're not doing that, but we
00:02:41.280
absolutely can. So you might write
00:02:44.160
something like this where you have a
00:02:46.239
before action and you want to lock down
00:02:48.319
this controller to admins only and you
00:02:51.360
would say before action require admin.
00:02:53.360
You'd implement that logic in a private
00:02:56.000
method to require the admin.
00:02:59.360
But I would propose that you could use a
00:03:02.319
class method instead that stands out a
00:03:04.800
bit better and say admin access only and
00:03:08.159
have that be the focus. So instead of
00:03:10.959
implementing the logic for this in your
00:03:12.720
controller, we could extend this um and
00:03:16.560
put this in an authorization concern
00:03:19.280
that implements a class method that just
00:03:21.599
simply calls the before action right
00:03:24.159
there instead. So it's an extra little
00:03:26.480
layer over top of the before action, but
00:03:29.440
allows you to put better terminology
00:03:32.000
into your uh controllers and um and
00:03:36.000
emphasize the right stuff uh even more.
00:03:40.400
But there's another benefit to this with
00:03:42.720
class methods. They allow you to pass in
00:03:45.040
arguments. So if you went to build rate
00:03:48.080
limiting yourself, you might say before
00:03:50.400
action rate limit, but the symbol
00:03:52.720
doesn't allow you to pass in any
00:03:54.080
arguments. So if these two controllers
00:03:56.480
need different rate limiting um
00:03:59.439
settings, you'd have to build two
00:04:01.439
different rate limit methods in those
00:04:03.760
separate controllers.
00:04:05.760
But if we look at how Rails implements
00:04:08.400
rate limiting, it is a rate limit class
00:04:11.519
method. Uh you don't see the class
00:04:13.760
method part of the concern because
00:04:15.120
there's a lot of documentation in a
00:04:16.880
comment above. But you see that because
00:04:19.840
of Ruby um having the scope of this
00:04:23.919
before action and the lambda is within
00:04:25.919
the method, we can reference to and
00:04:28.800
within and the other options right
00:04:30.800
there. and it allows us to use that and
00:04:33.440
customize how it works.
00:04:36.240
So why don't we go back to our admin
00:04:38.800
access only method and uh refactor that
00:04:42.479
to take advantage of those arguments.
00:04:45.199
So we could say let's move the message
00:04:47.840
so you can customize it as an argument
00:04:50.160
there. We can keep the other options and
00:04:53.600
use that to pass along for example the
00:04:56.320
only or um you know if or unless or
00:05:00.000
whatever as options for before action
00:05:02.320
but we can pull out our specific
00:05:04.160
features um as options like message
00:05:07.600
here. So we could say now admin access
00:05:10.639
only get out or I'm calling the police
00:05:13.199
uh if somebody tries to access the admin
00:05:15.280
controller instead.
00:05:18.080
Um, this also is helpful if you maybe
00:05:22.320
want to uh redirect to a different route
00:05:25.600
depending on the situation. You could
00:05:27.680
pull out the route that is redirected to
00:05:30.479
as an argument too.
00:05:33.360
Another thing is you should not be
00:05:34.960
afraid to add your own folders to the
00:05:37.680
app folder in your Rails app. Here is an
00:05:41.520
example that has API clients, uh UI
00:05:45.199
components, admin resources, notifiers
00:05:48.240
for notifications, maybe uh
00:05:50.080
authorization policies like for pundit
00:05:53.120
um and active record validators. We can
00:05:55.759
add folders that are part of our
00:05:59.039
application and the domain it's trying
00:06:00.720
to solve and organize things there. You
00:06:03.840
don't have to shove things into the
00:06:05.520
existing folders. You have lots of
00:06:07.680
options uh to do that. But it feels kind
00:06:10.400
of like wrong maybe at first when you're
00:06:13.520
like it needs to just fit in one of the
00:06:15.360
buckets that Rails gives me. It does
00:06:17.440
not.
00:06:19.520
So here's an example. Uh in our learn
00:06:21.840
hotwire course application um we have
00:06:25.440
couple API clients uh and instead of
00:06:28.560
actually pulling in the gems for those
00:06:31.520
APIs, we have our own custom API client.
00:06:34.800
What's really nice about this is for
00:06:36.560
example with GitHub we you purchase the
00:06:40.000
course and you add your GitHub account
00:06:42.000
and we'll just invite you to the
00:06:44.720
organization so you can access the
00:06:46.560
source code and we could use the Octokit
00:06:49.280
gem for that but it implements every
00:06:52.000
single API endpoint that GitHub has. we
00:06:55.039
only need one and that gem updates
00:06:57.680
constantly because GitHub is changing a
00:06:59.680
lot of their APIs on a regular basis but
00:07:03.280
our code doesn't have a dependency on
00:07:05.840
anything else no third party
00:07:07.199
dependencies and we only have to
00:07:08.720
implement one thing and that's not going
00:07:10.639
to change very often so this is way way
00:07:14.000
more maintainable in the long run which
00:07:16.400
is nice
00:07:20.080
um we can also build our own custom
00:07:22.080
generators so Rails ships with a bunch
00:07:24.400
of generators. You might use a gem that
00:07:26.319
has generators. You can put generators
00:07:28.479
in your own application too. So of
00:07:31.039
course we can make a generator for API
00:07:33.840
clients and we can run rails generate
00:07:36.160
generator for API client to generate the
00:07:38.639
generator. Um, and this is super useful
00:07:44.000
because out of the box it's going to
00:07:45.840
inherit from uh the named base generator
00:07:49.440
class. And this is responsible for
00:07:52.160
handling that first argument when you
00:07:54.479
run like rails generate model or
00:07:56.639
migration.
00:07:58.160
You give that first argument the name of
00:08:00.160
the model and it includes a bunch of
00:08:02.720
helpers on the file path for it. Um, you
00:08:06.879
know, all kinds of different things. And
00:08:08.319
I'll show you uh how we can use that
00:08:10.800
here.
00:08:14.960
So our implementation for building um
00:08:18.240
the generator can simply say here's
00:08:20.879
where you can find all the templates. Um
00:08:23.280
we can add like options as well. So you
00:08:25.599
can say d- URL and specify the base URL
00:08:29.199
um for the API like api.github.com
00:08:31.120
github.com
00:08:32.640
and then any public methods in here will
00:08:35.360
be executed and they will take the
00:08:38.640
template out of the um that directory
00:08:42.240
and then copy it into the application at
00:08:44.880
the using like the file path helper
00:08:47.760
which will also handle things like
00:08:49.839
namespacing automatically for you and
00:08:52.240
that makes this really really easy to uh
00:08:54.880
to build your own tools in your
00:08:57.120
application which is nice.
00:08:59.760
And speaking of generators, you can
00:09:01.600
override templates from any generator uh
00:09:04.959
in Rails or gems as well.
00:09:09.120
So for example, um scaffolds typically
00:09:12.240
have the notice at the top for every
00:09:14.560
show action which you probably have
00:09:16.959
extracted into your application layout
00:09:19.440
and have one single place for notices.
00:09:21.440
So this would end up generating notices
00:09:23.440
twice and displaying them twice. So you
00:09:25.680
can just override this file under lib
00:09:27.839
templates erb scaffold show.html.erb.tt
00:09:32.800
just delete that line and now your
00:09:34.640
scaffolds won't do that. Um and you can
00:09:37.120
take this further for example uh we will
00:09:39.920
take like the index and we'll add
00:09:42.240
pagionation to it or filtering and s uh
00:09:45.360
sorting and search. um you can do all
00:09:47.920
kinds of stuff with it and then actually
00:09:50.160
rather than just like saying ah we don't
00:09:52.320
use scaffolds anymore, you've made them
00:09:54.480
work for your team and for your
00:09:55.920
application and then it'll be all
00:09:57.440
consistent and you can keep using those
00:09:59.200
features.
00:10:01.440
Um let's also talk about concerns. Uh
00:10:04.399
typically when we think about concerns,
00:10:06.959
think about reusable code. Um, so you
00:10:10.240
might make a sortable module and it adds
00:10:13.600
a couple sorting functions and this can
00:10:15.440
be used in literally any model in your
00:10:17.680
Rails app. But
00:10:20.320
concerns can be used for organizing
00:10:22.560
features. Um, David open sourced
00:10:25.360
Campfire and one of the things that
00:10:27.519
caught my attention in there was they
00:10:30.000
organized parts of their models by
00:10:33.440
feature using concerns and rather than
00:10:36.399
putting them in the concerns folder,
00:10:38.560
they'll put them in the name space. So
00:10:42.000
when we include billing, it will first
00:10:44.399
check to see if there's a user billing
00:10:46.320
module or it will fall back to looking
00:10:48.800
in the top level namespace. Same with
00:10:51.839
mentions. So we can go implement all of
00:10:54.320
our billing code under user billing. And
00:10:57.360
this is going to live in the
00:11:01.839
app models user folder instead of app
00:11:05.680
models concerns because it's not reused
00:11:07.839
by anybody else. But it can have all of
00:11:10.000
our building code in one place which
00:11:13.120
tends to get messy if you have it all in
00:11:15.760
one file where you might have functions
00:11:18.240
and scopes and constants or whatever in
00:11:21.519
various places in the file. You can put
00:11:24.079
it all in one place now and it doesn't
00:11:26.399
mix together with the other uh features
00:11:29.120
in there. Same thing goes for like
00:11:32.000
implementing mentions, including the
00:11:34.160
attachable module, and then maybe
00:11:36.240
setting the uh the text representation
00:11:39.600
with the user's name as a default if
00:11:42.079
there wasn't a caption. And you might
00:11:44.240
want a very a different one of these. If
00:11:46.880
you have say blog posts with titles, you
00:11:49.839
could do the same thing, but change the
00:11:51.600
implementation of the text
00:11:54.079
representation.
00:11:56.320
So let's take a look at an example um
00:11:59.519
where uh we'll take and use a bunch of
00:12:02.880
th these techniques and build a
00:12:05.839
cloudflare turnstyle integration.
00:12:09.279
So you might do this as your first
00:12:12.000
implementation where we need to verify a
00:12:14.560
turn style before we create the user
00:12:16.480
record. Um and there's a couple issues
00:12:18.959
with this. Uh so first it's kind of made
00:12:22.240
your controller a bit me more messy but
00:12:25.839
also if turnstyle validation fails we
00:12:28.959
haven't even attempted to validate the
00:12:30.800
user. So they would fill in their stuff
00:12:34.399
for turnstyle that would rerender the
00:12:36.800
form with an error but we wouldn't
00:12:38.560
actually check and show any errors for
00:12:40.560
the user's email or password or
00:12:42.720
whatever. So then they might have to
00:12:44.639
fill out turn style like three or four
00:12:46.480
times and it would be annoying and it
00:12:47.839
would be nice if it was all integrated
00:12:49.760
into the same flow.
00:12:52.480
So what if we made that a concern? This
00:12:55.040
in this case would be a reusable concern
00:12:57.600
because we could use this for any model.
00:12:59.920
So we can make uh challenges turn style
00:13:04.079
and the implementation for that would
00:13:05.839
look something like this where we make a
00:13:07.440
module adds a virtual attribute for the
00:13:09.839
challenge token and a validation. But
00:13:12.800
we're going to make up a custom
00:13:14.399
validation called turnstyle and set it
00:13:16.720
to true to run that. Rails will look for
00:13:20.160
a class called turnstyle validator
00:13:23.839
and we can implement that um in our own
00:13:27.680
custom folder for app validators and
00:13:30.720
then it's going to be given the record
00:13:33.440
the attribute and the value of it which
00:13:36.000
we can send over to the turn style API
00:13:38.959
and ask the JSON response for success
00:13:42.079
was it true or false if it was not
00:13:44.320
successful we can add a record or an
00:13:46.720
error to the record um and display that
00:13:50.000
in uh the form.
00:13:53.040
So then of course we can use our
00:13:55.440
generated generator for the generator of
00:13:58.320
API clients uh and create that and we
00:14:01.360
just call it turnstyle that's going to
00:14:03.600
then create this file which we can add
00:14:06.639
the base URI to the turnstyle API. We
00:14:10.399
have the secret we can look up from
00:14:12.399
either environment variables or Rails
00:14:14.560
credentials. Then we just need to make a
00:14:16.800
post request to the endpoint. Give it
00:14:19.600
our secret, our token, and the optional
00:14:22.160
IP address, which if we use current
00:14:24.639
attributes, we don't have to also pass
00:14:26.880
that along to the model. We can just
00:14:28.720
reference that after we save it in the
00:14:31.279
controller in a before action.
00:14:34.800
So then this allows us to come back to
00:14:36.720
our controller and go back to a very
00:14:39.279
simple implementation. We don't have to
00:14:41.680
have extra methods in here or anything.
00:14:43.920
It's now handled automatically by active
00:14:46.160
records validations. The only change we
00:14:48.560
would do here is our signup params would
00:14:51.199
need to um permit the challenge token on
00:14:55.120
that user. Um so we just accept another
00:14:58.079
parameter just like normal um which is
00:15:00.399
easy and fits the entire pattern of
00:15:02.720
everything else. So turnstyle now feels
00:15:05.600
like assigning any other attribute um
00:15:08.079
which is great. And then of course you
00:15:10.959
can extract this out as a plugin for
00:15:13.120
rails.
00:15:14.800
So we can take that concern and we can
00:15:17.519
make it a little bit more mod but
00:15:19.120
modular by adding a challenges with
00:15:22.160
class method that takes the service
00:15:24.240
name. So then we can support turnstyle
00:15:27.279
recapture h capture and all our um rails
00:15:31.279
plugin would need to do is add those
00:15:32.959
extra validators and you're off to the
00:15:35.440
races. Um, and then we can use active
00:15:37.760
support onload, active record in order
00:15:40.480
to uh make that added to rails whenever
00:15:44.399
it boots and is required and loaded.
00:15:48.800
Speaking of extending Rails,
00:15:51.600
um, a couple weeks ago, there was this
00:15:53.600
post on Reddit. Uh, 70 companies you
00:15:56.560
didn't know were using Rails in 2025.
00:15:59.279
The first comment on there was, "Go
00:16:00.880
Rails." What a surprise. And I was like,
00:16:04.160
you know what? Imagine the scandal if we
00:16:06.720
were using WordPress. Uh, that would be
00:16:08.560
pretty funny. So, I got looking at the
00:16:11.199
Go Rails admin dashboard and it got me
00:16:14.000
thinking,
00:16:16.000
you know, what if we took this joke too
00:16:18.320
far? Uh, and of course, Tenderlove like
00:16:22.000
16 years ago made Fuby, which some of
00:16:24.240
you guys are familiar with, that
00:16:26.079
actually is a Ruby gem that compiles PHP
00:16:30.399
and a C extension to talk to PHP from
00:16:33.040
Ruby. Um, I didn't bother updating that
00:16:36.560
to work with the latest version of PHP,
00:16:39.040
but I did wonder what if we could just
00:16:41.040
render PHP right from your controllers.
00:16:44.399
So, obviously,
00:16:46.320
brew install PHP. Let's make this
00:16:49.279
actually work because why not? Um, and
00:16:52.480
you'd be surprised. It's these four
00:16:54.639
lines of code. Uh, you add a renderer,
00:16:57.680
you register PHP as a renderer. the
00:17:01.120
options you saw in the render call, the
00:17:03.920
ones for PHP will be put in the uh first
00:17:06.640
argument there, the PHP code. The rest
00:17:09.120
of it is the second argument, which we
00:17:11.039
don't care about. And then we just tell
00:17:13.039
Rails uh we're going to return HTML in
00:17:15.679
the response. And then we'll shell out
00:17:17.679
to the PHP um command line. Just give it
00:17:20.720
that PHP code. Uh and we can open this
00:17:23.360
up in our browser. And hello world from
00:17:25.600
PHP works, which is awesome. Uh, and if
00:17:28.720
you don't believe me, we can try PHP
00:17:30.880
info. And you will see that it's running
00:17:33.520
the latest version of PHP 8.4 on my uh
00:17:37.360
on my Mac here installed with Homebrew,
00:17:40.559
which is great. Uh, but obviously we got
00:17:44.320
to take this joke too far. So, we need
00:17:46.160
to go deeper. So, why not support PHP
00:17:50.080
templates in Rails?
00:17:53.039
So, in order to do that, it's helpful to
00:17:55.039
understand the uh syntax of the file
00:17:57.200
name. So we have index which is the
00:17:58.799
action name. HTML is the format and ERB
00:18:02.240
is actually the template handler like
00:18:04.160
the processor of that.
00:18:06.960
So we need obviously index.php.php
00:18:12.880
and we can register that stuff and and
00:18:15.360
make it work of course. So in order to
00:18:18.400
tell Rails that we're looking for the
00:18:19.919
PHP format we add PHP that's normally
00:18:23.520
HTML of course. Um so we do that. We try
00:18:27.120
and access that, but the Rails logs say,
00:18:30.480
"The hell are you talking about?" You
00:18:32.480
know, we don't know what that is. So,
00:18:35.440
one line of code, you say, you know, PHP
00:18:38.000
is just an alias for for HTML content.
00:18:42.240
And, uh, yeah, Rails now understands
00:18:44.480
PHP.
00:18:46.000
Um, so this is good. It gets us to the
00:18:49.120
point where we can start processing
00:18:52.320
those files. So we can register a
00:18:55.039
template handler for PHP ERB files. And
00:18:59.039
then it needs um a callable object. It
00:19:02.240
could be a lambda. It could be a class
00:19:03.760
with the call method on it that'll give
00:19:05.679
us the template and it will give us the
00:19:07.440
source. And then what do we do? Um we
00:19:10.160
need to process it. But how does this
00:19:11.760
stuff work? Well, if we throw a binding
00:19:15.440
IRB in there, we can look at this
00:19:17.039
template variable and call identifier.
00:19:19.520
And you'll see this is the full path uh
00:19:21.919
to that file on disk. You know, my I
00:19:24.799
love PHP user on my Mac, uh PHP on Rails
00:19:28.400
app and so on. Um and if we poke around
00:19:32.559
inside the Rails source code, you'll see
00:19:35.200
uh the other handlers in there. HTML is
00:19:37.600
one of them. But this is kind of weird,
00:19:39.760
right? Like uh on line 7, instead of
00:19:42.559
returning Ruby code, it's returning a
00:19:44.880
string of Ruby codes. That's that seems
00:19:47.919
weird, but what actually is happening
00:19:49.919
here is this is how Ruby or Rails
00:19:52.640
compiles your templates into your Rails
00:19:55.760
app. So, what it's doing is you're
00:19:59.039
returning PHP or Ruby code in this case.
00:20:03.280
Um, and it's going to take that and
00:20:04.720
define a method so that instead of
00:20:07.120
having to read from disk and evaluate
00:20:08.880
the ERB file every time, it's going to
00:20:10.960
evaluate it and then define a method
00:20:12.960
that it can just call much quicker. So,
00:20:15.760
we need to go back to our template
00:20:18.000
handler and then change it to return a
00:20:20.559
string of Ruby code. We'll shell out to
00:20:23.039
PHP to evaluate the file. We can mark it
00:20:25.679
as HTML safe so it trusts the content
00:20:27.919
for it um or whatever and then return
00:20:30.880
that back.
00:20:32.720
So, let's test it out. We'll make an
00:20:35.360
index.php PHP ERB file. We'll put some
00:20:38.880
PHP tag to print out a paragraph and a
00:20:41.120
hello world and uh some ERB for good
00:20:44.080
measure because we need to make sure
00:20:45.679
that still works and we can include
00:20:47.600
those Ruby things in there. And we try
00:20:51.360
it and it works. Um sort of the hello
00:20:55.440
world gets printed out. There's a space
00:20:57.039
in between so we know our paragraph tag
00:20:59.120
is actually being rendered correctly. Um
00:21:02.400
but we still need to handle the ERB
00:21:04.159
portion of this. So um we can come back
00:21:08.799
to our method uh for the handler and
00:21:12.080
we'll capture the output from PHP and
00:21:15.039
we'll just forward along to the regular
00:21:16.640
ERB processor. So we'll give it the
00:21:19.120
template but the modified source code
00:21:21.600
from PHP and voila
00:21:26.159
we have uh the params being printed out
00:21:28.640
now as well. So now we have PHP and ERB
00:21:32.240
working perfectly together uh which is
00:21:34.400
great. But of course, we need to make
00:21:36.240
sure this works with partials and
00:21:38.000
layouts and stuff, too. So, why not make
00:21:40.080
a layout that prints out a copyright in
00:21:42.960
the footer, but we'll use PHP to to
00:21:45.280
render the date out there. And there we
00:21:48.159
go. So, that's great,
00:21:52.559
but why, you know, why are we doing
00:21:55.120
this? Um, oh, I meant to delete this
00:21:58.000
slide.
00:21:59.360
Uh
00:22:02.480
so
00:22:04.159
this is exactly how things work in
00:22:07.039
common gems and everything. So
00:22:08.640
index.json.jbuilder
00:22:10.960
same exact concept here. Um and the
00:22:13.679
Jbuilder gem if you open up the rail tie
00:22:16.320
has an onload hook for action view and
00:22:19.120
it says hey let's register the Jbuilder
00:22:21.440
template handler and we'll uh process
00:22:24.960
that with the Jbuilder handler class and
00:22:28.880
um it actually also overrides the Rails
00:22:31.919
templates. Um, so Rails itself doesn't
00:22:35.520
actually this the scaffolding and stuff
00:22:37.520
in Rails doesn't actually support JSON,
00:22:40.320
but that is added by Jbuilder. Jbuilder
00:22:43.120
is a default gem in your gem file. So
00:22:45.280
that's why you always see your scaffolds
00:22:47.200
create the HTML responses plus the JSON,
00:22:50.640
but it just overrides those templates as
00:22:52.799
well. Um, and it turns out as I was
00:22:56.159
looking at this, apparently I was the
00:22:58.000
last commit in this folder on Jbuilder,
00:23:00.640
which was uh funny. Um, but if you've
00:23:04.080
looked at inertia rails, this is exactly
00:23:07.679
how it works. So, render inertia, give
00:23:10.640
it a component name for reactor view um
00:23:13.679
in your props, and it's able to take
00:23:15.440
your props, convert them to JSON, and
00:23:17.679
serialize them and pass them over to
00:23:20.640
your your reactor view components. Uh,
00:23:23.919
and if you poke around their gem, same
00:23:26.320
exact thing. adds a renderer to action
00:23:28.880
controller for inertia and then hand
00:23:31.120
hands it off to be processed to create
00:23:33.440
the response. Um, and a lot of this I
00:23:37.440
was working on a gem recently called
00:23:39.440
phen PDF uh where I wanted to generate
00:23:42.400
PDFs using HTML and CSS. Um, but WKHTML
00:23:48.000
to PDF is a common gem that we use for
00:23:50.880
that or have used but it's now
00:23:52.720
deprecated. Uh there's Grover, which is
00:23:55.520
great, and it talks to Chrome, like
00:23:57.919
headless Chrome, using Puppeteer to
00:24:00.960
export uh PDFs out of Chrome. The
00:24:04.000
annoying part about that is it requires
00:24:06.080
uh Node.js to be installed to have Rails
00:24:10.080
talk to Node to talk to Chrome to talk
00:24:12.400
back and it's kind of uh convoluted.
00:24:16.080
And I wanted no NodeJS. There's no in
00:24:18.799
the name for a reason. Um, and so Phum
00:24:22.960
is a a gem that you may be familiar
00:24:25.440
with, but it basically implements
00:24:27.039
Puppeteer um using pure Ruby. So, it
00:24:29.919
implements the CDP protocol for Chrome.
00:24:32.640
And I was like, you know, this seems
00:24:34.240
like a good idea uh in order to build a
00:24:37.440
nodeless version of exporting PDFs and
00:24:40.080
Rails. And what I wanted to do was this.
00:24:44.159
I wanted to be able to visit a route and
00:24:47.120
have it render HTML, but then also
00:24:50.640
capture the HTML, boot up headless
00:24:52.799
Chrome, insert it into a new tab, export
00:24:56.000
the tab as a PDF, capture the PDF, and
00:24:58.799
then send it back to the browser. So,
00:25:00.480
when you make a request to one of these
00:25:02.720
routes, we end up kind of double
00:25:04.640
rendering. Um, but we render the HTML
00:25:07.600
just so that we can do that. And then
00:25:09.279
what we really want to send back to the
00:25:10.880
user is the PDF. So, uh, as I was
00:25:14.480
working on this, what I was like, you
00:25:16.240
know, how do I do this cleanly in the
00:25:19.039
controller and make it really fit into
00:25:20.960
Rails? Uh, and I came up with this,
00:25:23.279
which led me into all these nonsense
00:25:26.159
things like, uh, render PHP. Um, but
00:25:29.919
this is how the gem works now. You can
00:25:31.760
just simply say render farm PDF. It will
00:25:34.480
actually effectively call the format
00:25:36.480
HTML behind the scenes. And then you can
00:25:39.840
pass in options with the curly braces um
00:25:42.559
for PDF specs of like maybe you want it
00:25:45.360
to be landscape mode or whatever. But
00:25:48.080
then it gets the PDF back from Chrome
00:25:51.360
and we can take the extra options like
00:25:53.520
disposition and file name and use those
00:25:55.840
with the send data method to give the
00:25:58.240
hints to the browser that yes uh we're
00:26:00.480
giving you a PDF but we'd like you to
00:26:02.240
display it inline and here's a suggested
00:26:04.640
file name for it. So it ends up being uh
00:26:08.240
really elegant and just kind of fits
00:26:09.760
with Rails and you don't even have to
00:26:11.360
worry too much about calling this mod
00:26:14.400
like the class from the gem or anything
00:26:16.799
like that. It's just kind of seamless.
00:26:20.559
So that's about it. Um but obviously
00:26:24.400
Marco's been working on Herb making ERB
00:26:27.919
very HTML aware. So I'm going to need
00:26:29.919
you guys to help me convince him to add
00:26:31.520
PHP support to it. So, uh, not like he
00:26:35.679
needs anything more to work on or
00:26:37.440
anything, but, uh, I feel like we need
00:26:39.760
that now. Um, so yeah, thanks