Les variables comme $1 sont remplies après l’exécution d’une correspondance de regex. Simplifions un peu votre exemple :
s.gsub('original(value)', "replacement#{$1}")
L’expression "replacement#{$1}" est évaluée avant que la fonction gsub ne soit appelée. Ainsi, $1 contiendra la valeur résiduelle d’une tâche de regex précédente. (C’est pourquoi votre deuxième tentative fonctionne : elle récupère $1 de la première tentative)
Plusieurs options permettent de résoudre ce problème. gsub offre de nombreuses fonctionnalités différentes.
Ma préférence va à la transmission d’un bloc à gsub. Le bloc est évalué après la correspondance de la regex, de sorte que $1 fonctionne comme prévu :
s.gsub('original(value)') { |match| "replacement#{$1}" }
Vous pouvez également utiliser la fonctionnalité de « référence arrière » de gsub. Je ne suis pas fan de cette syntaxe, mais elle fonctionne. Au lieu d’utiliser "replacement#{$1}", utilisez 'replacement\\1' (ou \\2, \\3, etc.)
s.gsub('original(value)', 'replacement\\1')