Skip to content

Conversation

@fpsvogel
Copy link
Contributor

@fpsvogel fpsvogel commented Oct 31, 2025

This is a 🙋 feature or enhancement.

Summary

Adds a Liquid tag ruby_render that renders a Ruby expression or a simple Ruby component. "Simple" meaning without content in a block:

{% ruby_render MyComponent.new(title: "My Title") %}

Context

Resolves #976

Manual test

I created a new site with bridgetown new test_site -t liquid, then created two Ruby components:

Then I rendered these two components plus a Ruby expression (to show that's possible too) at the top of the blog post:

{% ruby_render RubyComponent.new %}
{% ruby_render TemplatedComponent.new(name: "Felipe") %}
{% ruby_render "A Ruby string with the random number #{rand(100)}" %}
image

Added section on website "Ruby Components" page

Adapted from the section on liquid_render

image
site:,
page: payload["page"],
cached_partials: self.class.cached_partials,
resource: document,
Copy link
Contributor Author

@fpsvogel fpsvogel Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this becausepage is a Bridgetown::Drops::ResourceDrop, not the full resource that is needed to create a RubyTemplateView in the new tag below.

There's also context["resource"], but it too is a Bridgetown::Drops::ResourceDrop.

require "helper"

class TestTags < BridgetownUnitTest
using HashWithDotAccess::Refinements
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried nested Structs too, instead of .as_dots below, but that didn't make the tests in this file run noticeably faster.

Comment on lines -12 to +14
def create_post(content, override = {}, converter_class = Bridgetown::Converters::Markdown) # rubocop:disable Metrics/AbcSize
def create_post(content:, override: {}, converter_class: Bridgetown::Converters::Markdown, page_title: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
Copy link
Contributor Author

@fpsvogel fpsvogel Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made these into keyword arguments in a separate commit to avoid one gross call to this method. If it's preferable to keep the positional args, I'm fine omitting that commit.

I added the :page_title arg because one of the new tests uses it in the component that it renders (see the expectation below). I'm passing in the page title rather than parsing the YAML header because that seems like overkill for just one test case that needs the page title to be available.

Comment on lines +25 to +31
resource: {
site:,
data: {
title: page_title,
layout: "default",
},
}.as_dots,
Copy link
Contributor Author

@fpsvogel fpsvogel Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:resource is needed for testing ruby_render (below).

@fpsvogel fpsvogel force-pushed the ruby-render-liquid-tag branch from 8defa34 to af3dd81 Compare October 31, 2025 04:16
@fpsvogel fpsvogel force-pushed the ruby-render-liquid-tag branch from af3dd81 to 3b92fbe Compare October 31, 2025 04:19
Comment on lines -142 to +183
fill_post("test")
fill_post(code: "test")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of the changes in this file are the switch to keyword arguments.

# @return [String]
def render(context)
view_context = Bridgetown::RubyTemplateView.new(context.registers[:resource])
result = eval(@ruby_expression) # rubocop:disable Security/Eval
Copy link
Contributor Author

@fpsvogel fpsvogel Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to avoid eval, it might be better to use a non-Ruby syntax for the Liquid tag contents (such as the one we previously discussed in the GH Issue), because the other way to parse the contents (apart from eval) is via regex, and trying to emulate Ruby parsing via regex seems difficult to do well, even if we allow nothing other than component instantiation.

If the regex route is preferable, I assume it's because eval is insecure, but I would be curious to know how specifically, for this use case.

@fpsvogel fpsvogel marked this pull request as ready for review October 31, 2025 12:20

## Rendering Ruby Components from Liquid Templates

You can use the `ruby_render` helper from Liquid templates to render Ruby components.
Copy link
Contributor Author

@fpsvogel fpsvogel Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ruby components are the primary use case, so I'm not sure if it's worth mentioning that it's possible to render any Ruby expression—if it's not a component (i.e. doesn't have #render_in) then it's rendered with #to_s (see above).

@jaredcwhite
Copy link
Member

Some cool stuff in here @fpsvogel. I'll want to think on this a bit…one of the promises of Liquid is that you're working within a "sandboxed" environment and all the values/tags/etc. live with the Liquid environment. If we punch a hole right into Ruby land with eval, it could end up with unexpected weird behavior. I'm not worried about the security aspect per se as Bridgetown isn't built around a "user-contributed" Liquid template content model, and there are no guarantees of safety in that respect, but there could be other gotchas.

@jaredcwhite jaredcwhite added this to the 2.2 milestone Dec 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants