Gsub выдает разные результаты при запуске одного и того же кода

Переменные вроде $1 заполняются после выполнения сопоставления с регулярным выражением. Немного упростим ваш пример:

s.gsub('original(value)', "replacement#{$1}")

Выражение "replacement#{$1}" вычисляется до вызова функции gsub. Поэтому $1 будет содержать значение от какой-то предыдущей операции с регулярным выражением. (Именно поэтому ваша вторая попытка сработала — она использовала $1 из первой попытки).

Есть несколько способов решить эту проблему. У метода gsub много разных возможностей.

Мой предпочтительный вариант — передать в gsub блок. Блок выполняется после того, как регулярное выражение найдено, поэтому $1 будет работать так, как вы ожидаете:

s.gsub('original(value)') { |match| "replacement#{$1}" }

Или можно использовать функцию «обратной ссылки» в gsub. Мне не нравится этот синтаксис, но он работает. Вместо "replacement#{$1}" используйте 'replacement\1' (или \2, \3 и т. д.).

s.gsub('original(value)', 'replacement\1')