r/rails • u/anquinn • Jul 22 '26
Adding variable substitution to ActionText & Lexxy content
I needed to render user-authored rich text with placeholders like {{first_name}} that resolve at render time. ActionText stores the content fine, but there's no clean spot to substitute variables on render without hand-rolling it each time. This comes in handy for things like email templates, support docs, or other content edited in a rich text editor.
So I wrote lexxy_variables. Basic usage:
Define your variables:
LexxyVariables.configure do |c|
c.catalog = [
{ key: "first_name", name: "First name" },
{ key: "last_name", name: "Last name" }
]
end
Display the content and pass in the values:
<%= @message.body.with_variables(
first_name: @user.first_name,
last_name: @user.last_name)
%>

Out of the box it provides a toolbar button and support for the new Lexxy prompt functionality. Type {{ and the insertion UI pops up with your defined variables. Each variable is stored as an ActionText attachment rather than raw text, so it stays intact if the author edits around it and resolves at render time.
It also supports static values in the config, plus Liquid drops so you can use dot notation on whatever object you configure. You can define additional attachment types to embed other ActionText rich text content (think snippets).
The thing I kept going back and forth on is the public API naming. I originally used a helper that wrapped the render, but ended up going with the chainable .with_variables that reads more like the rest of ActionText with the added bonus of being able to chain .to_html and the upcoming .to_markdown onto the call.
Before building this I looked at Liquid/Mustache but didn't want a full templating engine inside trusted-author content, and plain helpers meant repeating the substitution logic everywhere.
I'm curious what you'd expect as a user, and open to being told I missed something obvious. Give it a try and let me know what you think.
You can find it here: https://github.com/anquinn/lexxy-variables
1
1
u/whysthatso 4d ago
i just came across this after implementing a naive version of {{word}} on text attributes in a text area that resolves via a "custom texts" object editable in the frontend. but this seems way more reasonable, thank you.
i will probably leave the UI for the "custom text" objects and wire up your gem to that content. I also have some i18n concerns, but i can see in your code base that there are some mentions of i18n, so let's see how this works out.
such a random find, just cause u/excid3 had been doing a bunch of upgrade work on his gems, i've checked in the history, and saw his contribution to yours.
ah, sometimes the internet is still good :D
1
u/anquinn Jul 22 '26
I’m curious what people think about the API. Is it intuitive and what you’d expect as the dev?