Using an "ORM" for HTML
I want to splurge some words about Rails templates. A freshly generated Rails project comes with ERB-based templates for its views. This is totally fine. I've been using them for years now, and I can build nice things with it, I dare say.
Rails has some good conventions and as long as you stick to the naming conventions of views, you'll have a good time. The files are mostly just HTML with some <%= ERB tags %> here and there. It is easy to spot these tags and even easier when your editor correctly highlights them. For the rest, it's all just HTML which is the same stuff that you find in your browser dev tools.
Anyway, keep reading if you're worried more about the code itself than about what you make with it. A lot of developers have this problem and so do I. Telling you that the defaults are fine and that you shouldn't waste any brainpower thinking too hard about it would be petty lame.
The first thing I did to "improve" my code was to start using ViewComponents which is a more object oriented approach to building little fragments of views that we all lovingly call components. These classes nicely encapsulate the logic and data so it's easier to see what you're doing without having to open half a dozen files. Instead, we now have just two files that we are mostly concerned about, the class and the template. Now technically you could use any templating language but of course I stuck with ERB to minimize the number of languages.
Now I don't know about other people, but the fact that view components originate from GitHub is obviously a very good reason to use it. However, as it turns out, you're still writing ERB. And that, my dear reader, is where the problems begin.
OK, to be fair, ERB is hardly a language: it's simply a way to embed Ruby into any kind of text file. That's almost literally what ERB means. And that's not even the real issue.
The issue is that you're writing HTML directly. That's similar to writing SQL directly. Sure, go ahead, tell me "ORMs are bad mkay", because everyone on the internet says so. Then close this browser tab and keep on writing that plain HTML and SQL.
But if you think that ORMs are good because SQL is not a real programming language, then you might see that HTML is not a real programming language either. And perhaps you should not write it either. That's not to say that it's useless to learn SQL or HTML, in fact I think you should most definitely learn them if you want to build anything decent. Would you prefer writing Ruby wirh ActiveRecord or SQL files with ERB?
Now there are also these alternative templating "languages" for Ruby such as Haml or slim, but these are also not real programming languages. In fact, I'd argue that they're even worse. They're neither Ruby nor HTML but something in between with their own quirks. Why do you think nobody writes CoffeeScript anymore? It's neither Ruby nor JavaScript, and the latter appears to be a "real" language now.
So then what's left? If you haven't guessed it by now, we need an ORM for HTML. A way to generate layout in our main programming language, Ruby. This will give us maximum power and control to organize our code in a way that is pleasing.
Luckily, there is such a thing for Ruby. In fact, over the past 20 years or so, there have been at least a dozen or so of these things for Ruby. Markaby being one of the first. The library I've been looking into is Phlex which combines HTML building with component classes and a good Rails integration.
I was playing around with Phlex last night and I converted an entire page header into Phlex components. I converted a bunch of things from ViewComponents:
- ButtonComponent
- BreadCrumbComponent
- DialogComponent
- DialogTitleComponent
- IconComponent
Let's start with all the good bits.
Writing everything in Ruby is much more powerful than only <%= embedding snippets %>
of Ruby in HTML files. It is much easier to apply programming techniques that make the code easier to maintain and reuse. Having the 'template' and rest of the code in the same file is very nice as well. I often say that this is one of the advantages of Javascript frontend frameworks such as Svelte or React. Yes, you can embed ERB templates in ViewComponents as well, but your still have to write HTML plus syntax highlighting may not work very well.
Using Ruby blocks works well for HTML since structurally it's pretty much the same thing. Instead of <div class="flex">
it's just div(class: "flex")
. The names are the same, the syntax is slightly different. Where it starts to be good is when you notice that it's super easy to extract fragments into methods, just like refactoring any other Ruby class. Try not repeating yourself in HTML templates, without using more than one file.
Another nice thing is that you get to use all the Ruby tooling, which in my opinion is better than any templating language can give you. Linting and formatting is always a bit of a struggle with ERB files (for me personally at least). My editor setup also struggles to correctly match blocks in ERB with tailwind classes so sometimes it things that an if
ends at the end
in the justify-end
class. Sometimes this also messes up the syntax highlighting or formatting. No such issues with Phlex, it formats and lints like any other piece of Ruby. Ruby-LSP also just works and that just gets better all the time.
Converting from ERB template to Phlex is straightforward since the HTML and its Ruby representation are structurally identical thanks to Ruby's blocks. There is already a library for it.
The bad:
- It kind of looks like HTML if you squint just hard enough, but it is still different from looking at "real" HTML. When you open the inspector in your browser, you see HTML and it's just different.
- When you start improving/refactoring/polishing the code, it starts to look less and less like HTML since it gets broken up in many pieces and again, that's not what it'll look like in the browser.
These two complaints are kind of similar. They are also the same kind of complaints that you'll see about ORMs.
Performance. Anyway, we can now finally get to what you all came here for: the benchmarks for the HTML fragment that I converted.
erb
is the original as is currently in productionphlex-in-erb
replaces all ViewComponents with Phlex, but still uses the original ERB partialphlex
has everything written in Phlex classes, no ERB at all
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [x86_64-linux]
Warming up --------------------------------------
phlex 95.000 i/100ms
phlex-in-erb 104.000 i/100ms
erb 100.000 i/100ms
Calculating -------------------------------------
phlex 1.236k (± 3.3%) i/s (808.99 μs/i) - 6.175k in 5.000971s
phlex-in-erb 1.177k (± 3.1%) i/s (849.26 μs/i) - 5.928k in 5.039281s
erb 1.025k (± 2.3%) i/s (975.28 μs/i) - 5.200k in 5.074317s
Comparison:
phlex: 1236.1 i/s
phlex-in-erb: 1177.5 i/s - same-ish: difference falls within error
erb: 1025.3 i/s - 1.21x slower
In this case, phlex happens to be faster than ERB, but this might not always be the case.
Small fragments with "static" HTML such as:
<div class="flex items-center justify-between" data-controller="page">
<button class="bg-red-500">Click me!</button>
<button class="bg-sky-500">Click me too!</button>
</div>
Have to be written in Phlex as:
div(class: "flex items-center justify-between", data: { controller: "page" }) do
button("Click me!", class: "bg-red-500")
button("Click me too!", class: "bg-sky-500")
end
The static HTML fragment cannot really be optimized as it can just be copied byte-for-byte. In the case of Phlex, converting the attributes from a Hash into a string is relatively expensive. Phlex tries to improve this by using some caching techniques, but it will never be as fast as just copying a string. Overall I think the performance should be comparable or perhaps better than ERB for most cases.
In any case, I think there's plenty of words on this page now and I think I've convinced at least myself that it could be a good idea to start using Phlex to see what it's really like.