Skip to content

SQ3-410 - fix: enable WordML content reusability with current_fragment helper#11

Closed
leandronsp wants to merge 2 commits into
masterfrom
sq3-410-payment-plan-mergetag-payment_plans_with_discounts-isnt
Closed

SQ3-410 - fix: enable WordML content reusability with current_fragment helper#11
leandronsp wants to merge 2 commits into
masterfrom
sq3-410-payment-plan-mergetag-payment_plans_with_discounts-isnt

Conversation

@leandronsp

Copy link
Copy Markdown

Background

Payment plan tables were disappearing from generated PDFs. Troubleshooting revealed the root cause was in Sablon PR #84 (2018), which changed WordML to store parsed DocumentFragment objects instead of strings.

The problem: Nokogiri's add_next_sibling moves nodes rather than copying them. When a WordML object was used once, its nodes were moved from xml.children into the document. On subsequent reuse, xml.children was empty, causing content to silently disappear.

The bug existed since Sablon PR #84 (Jan 2018) but went unnoticed because most code creates fresh WordML objects for each use. Payment plans are the possible unique case that truly reused the same object (to check other scenarios as well).

Changes

Added current_fragment helper method that creates fresh node instances on demand by re-parsing XML. This can be seen as not performant at first glance, but they are parsed only when needed, so I think we won't face performance issues here.

Also added a new test case that reuses the same table content twice to prevent regression.

PR senny#84 introduced a bug where WordML objects could only be used once.
The issue occurred because Nokogiri's add_next_sibling() moves nodes
rather than copying them, leaving xml.children empty after first use.

Changes:
- Add current_fragment() helper that creates fresh node instances on demand
- Update all_inline?() and add_siblings_to() to use current_fragment
- Add test case that reuses same table content twice to prevent regression

This fix maintains PR senny#84's architecture (storing DocumentFragment) while
solving the node reuse issue by lazy-parsing fresh fragments when needed.
@linear

linear Bot commented Nov 18, 2025

Copy link
Copy Markdown

@leandronsp

leandronsp commented Nov 18, 2025

Copy link
Copy Markdown
Author

Closing this PR after further investigation. The root cause is in our implementation, not in Sablon upstream.

Our code happened to work with the old Sablon version because it masks the bug by constantly creating new fragments. The memoize we introduced recently worked accidentally with the old upstream implementation.

However, the new Sablon upstream exposed the bug that our memoize introduced. Combined with Nokogiri's add_next_sibling side-effect (which mutates the object in memory), this became an explosive combination 💣 .

We decided the fix needs to be on our side by removing the problematic memoization, not by patching Sablon.

cc @jmnsf

@leandronsp leandronsp closed this Nov 18, 2025
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.

2 participants