Usando migrações Rails para manipular dados

Postado originalmente em codecrate.com

As migrações do Rails foram originalmente projetadas para manipular esquemas de banco de dados . Adicionar tabelas, colunas e índices são todos os casos de uso que são incrivelmente bem suportados e até mesmo oferecem suporte a recursos mais avançados, como retornar ao estado anterior do esquema. Manipular dados de produção também é uma ocorrência comum para desenvolvedores, mas como fazer isso no Rails?

Importar dados de arquivos simples, atualizar registros e limpar registros em um estado incorreto são eventos regulares. Muitos desenvolvedores lidariam com essas tarefas abrindo o console Rails em um servidor de produção e executando manualmente alguns comandos para cuidar dos negócios. Você pode realizar todas essas tarefas usando migrações Rails, o que é significativamente menos arriscado do que dar a cada desenvolvedor acesso direto ao seu banco de dados para executar scripts únicos ao vivo em seus dados de produção . O uso de migrações também fornece acesso fácil a revisões de código de branch de recursos e até mesmo a testes de unidade .

Aqui estão alguns protocolos que eu recomendo ao criar suas migrações de dados Rails:

Registrar todas as coisas

Muitas migrações de esquema têm um caminho claro para a reversão se algo der errado, mas as migrações de dados são muito mais difíceis de reverter. É absolutamente essencial para suas migrações fornecer o máximo de informações de depuração possível para se / quando algo der errado.

Aqui está um exemplo simples que destaca o uso do say_with_timeauxiliar para auditar quanto tempo levou uma etapa de migração e quantos registros foram afetados. Certifique-se de retornar o número de linhas afetadas dentro do bloco !

def UpdateInvalidUsers < ActiveRecord::Migration
def up
say_with_time
"Updating invalid users..." do
count
= 0
User.find_each do |user|
say
"Updating user: #{user.id}"
user
.update!(status: 'valid')
count
+= 1
end
count

end
end
end

Estado de auditoria pré e pós-execução

O registro durante a execução de uma migração é bastante simples e também é muito útil registrar informações antes da execução sobre o estado atual do sistema. Por exemplo, registrar o número de registros inválidos antes do início da migração é um grande ponto de dados.

Uma boa migração de dados também deve assegurar que a operação foi concluída com êxito e gerar um erro se algo não for concluído conforme o esperado.

Tomando nosso exemplo anterior, aqui está uma versão expandida que adiciona um registro simples de pré e pós-execução, além de asserções.

def UpdateInvalidUsers < ActiveRecord::Migration
def up
say
"Found #{User.where(status: 'invalid').count} invalid records"

say_with_time
"Updating invalid users..." do
count
= 0
User.find_each do |user|
say
"Updating user: #{user.id}"
user
.update!(status: 'valid')
count
+= 1
end
count

end

invalid_count
= User.where(status: 'invalid').count
fail
"Found #{invalid_count} invalid records" unless invalid_count == 0
end
end

Reexecutável por padrão

Se uma migração de dados gerar um erro, o que você deve fazer? As migrações de dados devem ser projetadas para serem reexecutáveis ​​e se ocorrer algum erro, o Rails irá reexecutar automaticamente sua migração na próxima vez que você executar .rake db:migrate

Também é uma boa ideia definir classes internas em sua migração que sombreiam seus modelos de aplicativo e usam reset_column_informationpara garantir que sua migração tenha acesso à versão mais recente do esquema do modelo .

Vamos dar uma última olhada neste exemplo e ter certeza de que a migração pode ser executada novamente e não atualiza os registros que já foram corrigidos.

def UpdateInvalidUsers < ActiveRecord::Migration
def up
say
"Found #{User.where(status: 'invalid').count} invalid records"

say_with_time
"Updating invalid users..." do
count
= 0
User.where(status: 'invalid').find_each do |user|
say
"Updating user: #{user.id}"
user
.update!(status: 'valid')
count
+= 1
end
count

end

invalid_count
= User.where(status: 'invalid').count
fail
"Found #{invalid_count} invalid records" unless invalid_count == 0
end
end

Com essas técnicas fundamentais em seu currículo, você pode levar suas migrações de dados para o próximo nível e até mesmo explorar ferramentas adicionais como a gem migration_data que torna as migrações Rails ainda mais fáceis.