Ruby é interpretado. Variáveis são referências a dados, mas não os dados em si. Isso facilita o uso da mesma variável para dados de tipos diferentes.
A atribuição de lhs = rhs copia a referência no rhs, não nos dados. Isso difere em outros idiomas, como C, onde a atribuição faz uma cópia de dados para lhs do rhs.
Portanto, para a chamada de função, a variável passada, digamos x, é realmente copiada para uma variável local na função, mas x é uma referência. Haverá duas cópias da referência, ambas fazendo referência aos mesmos dados. Um estará no chamador, um na função.
A atribuição na função copiaria uma nova referência para a versão da função de x. Depois disso, a versão de x do chamador permanece inalterada. Ainda é uma referência aos dados originais.
Por outro lado, o uso do método .replace no x fará com que o ruby faça uma cópia dos dados. Se a substituição for usada antes de qualquer nova atribuição, o chamador também verá os dados mudarem em sua versão.
Da mesma forma, desde que a referência original esteja intacta para a variável passada, as variáveis de instância serão as mesmas que o chamador vê. Na estrutura de um objeto, as variáveis de instância sempre têm os valores de referência mais atualizados, sejam eles fornecidos pelo chamador ou definidos na função para a qual a classe foi passada.
A 'chamada por valor' ou 'chamada por referência' é confusa aqui devido à confusão sobre '=' Nas linguagens compiladas '=' é uma cópia de dados. Aqui nesta linguagem interpretada '=' há uma cópia de referência. No exemplo, você tem a referência passada, seguida por uma cópia de referência '=' que reprova o original passado na referência e, em seguida, as pessoas falando sobre ela como se '=' fossem uma cópia de dados.
Para ser consistente com as definições, devemos continuar com '.replace', pois é uma cópia de dados. Da perspectiva de 'substituir', vemos que isso é de fato passado por referência. Além disso, se percorrermos o depurador, veremos as referências sendo passadas, pois as variáveis são referências.
No entanto, se devemos manter '=' como um quadro de referência, de fato, conseguimos ver os dados passados até uma atribuição e, depois, não conseguimos mais vê-los após a atribuição, enquanto os dados do chamador permanecem inalterados. No nível comportamental, isso passa por valor desde que não consideremos o valor passado como composto - pois não seremos capazes de manter parte dele enquanto alteramos a outra parte em uma única atribuição (como essa atribuição altera a referência e o original fica fora do escopo). Também haverá uma verruga; nesse caso, variáveis em objetos serão referências, assim como todas as variáveis. Portanto, seremos forçados a falar sobre a passagem de 'referências por valor' e teremos que usar locuções relacionadas.