Nós tínhamos um problema intrigante. Nós sabíamos que as colunas de um modelo em particular precisaria de mudanças ao longo do tempo. Isto era facilmente resolvido com um campo JSON, mas nós também queríamos dar suporte a validações neste campo – validações que são atribuídas ao modelo no tempo de criação. Exemplo: Ontem nós criamos um registro válido para Veículo somente com um campo nome. Hoje, nós adicionamos um outro campo. Todos os novos veículos devem tê-lo. Se reabrirmos o registro de ontem, nós queremos que ele ainda esteja válido.

Este foi um esforço coletivo entre eu, Tom Rothe e Dmitry Parshenko. Depois de nós fazermos brainstorming e sessões de quadro branco, decidimos começar com um modelo Model chamado Vehicle. Ele contem um schema e uma description que define suas características.

O schema

O schema define atributos, nos dizendo seus tipos e valores padrões. Isto nos possibilita ter um conjunto diferente de atributos para cada registro da tabela. Campos obrigatórios também são declarados no schema para validações posteriores.

Nós queremos que cada nova instância de vehicle tenha seu esquema preenchido por um valor padrão declarado na tabela do banco de dados. Vamos criar nossa migration do Vehicle:

Nós estamos seguindo a estrutura do JSON Schema, mas somente as que são necessárias para o nosso objetivo.

Manipulando dados

Agora, para trabalhar tanto com novas instâncias ou com registros existentes do nosso modelo Vehicle, nós devemos criar nossa classe Vehicle:

O store_accessor define os getters e setters do schema. O serialize é necessário porque nós estamos usando JSONB (PostgreSQL). Para cada nova instância, iteramos sobre os atributos do schema e definimos os valores padrões de description.

Lidando com validações

Quando nós tentamos salvar ou atualizar uma instância de nosso Vehicle, nós iteramos sobre os campos obrigatórios que estão listados no schema:

Aqui só implementamos uma validação de presença, mas você pode adicionar outros tipos de validação.

Exibindo nossos registros

Para criar nossos formulários no HTML, nós usamos a definição do schema e iteramos sobre ela:

Repare que nós usamos o OpenStruct para fazer o form do Rails capaz de preencher os valores de registros existentes. Sem ele, nós sempre temos campos vazios quando estamos no formulário de edição.

Lidando com consultas

Como estamos usando PostgreSQL e JSONB, ganhamos o poder de consultas como estas:

Lembre que com JSONB, nós temos índices chamados GIN / GiST para ajudar com performance.

Conclusão

Nós agora somos capazes de entregar, em um banco relacional, múltiplos registros da mesma tabela, cada um com um conjunto diferente de atributos. Com a definição do schema e um pouco de mágica do Rails, a gente também habilita validações. Registros antigos continuam válidos e usáveis se o valor padrão do schema mudar, e consultas na descrição funcionam por causa do PostgreSQL a os campos JSONB.

Uma versão completa dessa ideia pode ser vista neste repositório do Github. Lembre de olhar os branches heitor, tom e dmitry, que têm abordagens diferentes para o mesmo problema. Contudo, o branch master combina os anteriores em uma solução mais completa.

Se você tiver quaisquer perguntas, entre em contato.

P.S.: Este código não é viável para produção. Nós nos abstivemos de refatorá-lo para uma gem / DSL, então os exemplos de código ficaram um pouco mais agradáveis.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

 

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.