Skip to content

Add test for compiled output with blocks#83

Merged
marcoroth merged 3 commits intomarcoroth:mainfrom
joelhawksley:fix-compilation-bug-test
Mar 18, 2026
Merged

Add test for compiled output with blocks#83
marcoroth merged 3 commits intomarcoroth:mainfrom
joelhawksley:fix-compilation-bug-test

Conversation

@joelhawksley
Copy link
Copy Markdown
Contributor

I'm not 100% sure about the nature of this bug, but we found this issue in our codebase and I distilled it down to this test case.

The solution is what Claude 4.6 and I came up with to make it pass. I am not very confident in the quality of the solution not knowing much about Herb internals, but this change did unblock us.

@styx
Copy link
Copy Markdown

styx commented Mar 16, 2026

I have the same issue, so 👍

@marcoroth marcoroth mentioned this pull request Mar 16, 2026
marcoroth added a commit that referenced this pull request Mar 16, 2026
Lock the `0.2.x` series to Herb `< 0.9` since it's not compatible, see
#83
marcoroth added a commit to marcoroth/herb that referenced this pull request Mar 18, 2026
This pull request updates `add_expression_block` in the `Herb::Engine`
to delegate to `add_expression` instead of dispatching directly to block
result methods. This makes `Herb::Engine` a true drop-in replacement for
`Erubi::Engine` when subclassed.

Previously, subclasses that overrode `add_expression` (like ActionView's
Erubi handler does) would not have their override apply to block
expressions, because `add_expression_block` bypassed `add_expression`
entirely. This meant subclasses also had to override
`add_expression_block` and `add_expression_block_end` to handle blocks
correctly. This pattern led to bugs like unmatched closing parentheses.

Now, `add_expression_block` sets an `expression_block?` flag and
delegates to `add_expression`. The default `add_expression` checks this
flag to route to the appropriate block result methods, preserving the
correct `<< (code do ... end)` output for standalone usage.

A new `@_expression_block_open_paren` flag tracks whether the opening
method added a parenthesis, so `add_expression_block_end` knows whether
to close with `)` or just emit `end` as regular code.

When no methods are overridden, the default `add_expression` routes
block expressions to the existing `add_expression_block_result` methods,
preserving Herb's smart block handling with proper parenthesization.

When a subclass overrides only `add_expression`, the same pattern
ActionView uses with Erubi, block expressions now flow through that
override automatically. The `expression_block?` predicate lets the
subclass distinguish blocks from regular expressions without relying on
a `BLOCK_EXPR` regex, since the parser already knows.

When a subclass overrides `add_expression_block` directly, that override
takes precedence and the delegation never happens.

Related marcoroth/reactionview#83
Related marcoroth/reactionview#84
@marcoroth marcoroth changed the title fix bug with blocks compiled output Fix bug with blocks compiled output Mar 18, 2026
Copy link
Copy Markdown
Owner

@marcoroth marcoroth left a comment

Choose a reason for hiding this comment

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

The main fix for this was done in this marcoroth/herb#1417.

I just rebased off of main, and we'll just merge in the new test case, thank you! 🙏🏼

@marcoroth marcoroth changed the title Fix bug with blocks compiled output Fix bug for compile output with blocks Mar 18, 2026
@marcoroth marcoroth changed the title Fix bug for compile output with blocks Fix bug for compiled output with blocks Mar 18, 2026
@marcoroth marcoroth changed the title Fix bug for compiled output with blocks Add test for compiled output with blocks Mar 18, 2026
@marcoroth marcoroth merged commit f5be4d8 into marcoroth:main Mar 18, 2026
24 checks passed
marcoroth added a commit to marcoroth/herb that referenced this pull request Mar 18, 2026
…ods (#1421)

This pull request moves expression handling logic from the compiler into
overridable engine methods, making `Herb::Engine` an easier drop-in
replacement for `Erubi::Engine`.

Previously, the compiler bypassed the engine's `add_expression` method
in two cases:

1. **Block expressions** (`<%= link_to do %>...<% end %>`), the compiler
called `add_expression_block` which dispatched directly to
`add_expression_block_result`, skipping any subclass override of
`add_expression`.

2. **Context-aware expressions** (inside `<script>`, `<style>`, or
attribute values), the compiler's `add_context_aware_expression` wrote
directly to `@engine.src`, bypassing the engine entirely (see #1419)

This meant subclasses that overrode `add_expression` (the Erubi pattern
used by ActionView) would not have their override apply to blocks or
context-aware expressions, leading to incorrect compiled output.

So the idea of this pull request is to move the logic from the compiler
into overridable engine methods so that subclasses (like ReActionView)
get correct behavior by only overriding `add_expression`. The compiler
becomes a thin dispatcher that routes tokens to the engine and no longer
manipulates `@engine.src` directly.

Resolves #1419 

Related marcoroth/reactionview#78
Related marcoroth/reactionview#80
Related marcoroth/reactionview#83
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants