Excluindo hashes aninhados

Ao lidar com hashes, o Rails expõe um método bacana chamado exceptque duplica o hash original e retorna sua cópia removendo os argumentos fornecidos.

Eu queria expor um operador semelhante para excluir hashes dentro de hashes dada a chave aninhada. Tentei abordar o problema duplicando ingenuamente o exceptmétodo do Rails :

class Hash
def except_nested_key(key)
dup
.except_nested_key!(key)
end

def except_nested_key!(key)
each
{ (k, v) v.delete(key) if v.is_a? Hash }

self
end
end

O problema com essa abordagem é que ambos dupe clonefazem uma cópia superficial do objeto, o que significa que, embora as referências do objeto original estejam sendo copiadas, os objetos aos quais essas referências se referem não estão. O que significa que, independentemente do método chamado, ambos executam ações destrutivas no objeto original.

Felizmente, podemos conseguir uma cópia profunda graças a Marshal . Da documentação Ruby:

“A biblioteca de empacotamento converte coleções de objetos Ruby em um fluxo de bytes, permitindo que sejam armazenados fora do script atualmente ativo. Esses dados podem ser lidos posteriormente e os objetos originais reconstituídos.”

Fazendo uso de Marshal, podemos reimplementar o método anterior da seguinte forma:

class Hash
def except_nested_key(key)
copy
= Marshal.load(Marshal.dump(self))

copy
.each { |(k, v)| v.delete(key) if v.is_a? Hash }
end
end