Introdução
Por uma questão de Legibilidade e Manutenibilidade escolherei trabalhar com enums do tipo string e não sendo do tipo integer, que é o mais comum. Michał Rudzki fez um artigo muito bom com um comparativo de perfomance, bem como indicando os pontos positivos e negativos entre os dois.
Rudzki deu dois grandes motivos para optar pelo enum com o tipo string: em enums baseados em string, você pode ler os dados do registro diretamente do banco de dados (o que favorece a legibilidade), e, você pode adicionar um enum facilmente a uma coluna existente, sem precisar se preocupar com possível perda de dados se utilizar o tipo string ( o que contribui na manutenibilidade).
Dito isso, vamos aplicação do enum. Antes de começarmos, pressuponho que você já tenha feita e migrada a tabela na qual deseja colocar o seu enum. A tabela - não só ela mas todo o MVC envolvido nos Usuários - que usaremos aqui foi criada da seguinte forma e com os seguintes campos:
rails g scaffold User name email birthdate:date
Depois disso eu dei o rails db:migrate (já tinha criado o banco de dados previamente) e então partimos para o ponto de colocação do enum que veremos à seguir...
1 - Gere a MIGRATION
rails g migration AddMaritalStatusToUsers marital_status:string:index
Onde:
- AddMaritalStatusToUsers - É o comando que indica pro Rails que você quer colocar uma nova coluna em uma determinada entidade do seu banco de dados. No seu caso será: AddNomeDoSeuEnumToNomeDaSuaTabela.
- marital_status - É o nome da coluna a ser adicionada a tabela Users
- string - O tipo de dado que essa coluna receberá
- index - Cria um índice para a coluna, o índice é uma estrutura auxiliar do banco que acelera buscas.
2 - Edite a MIGRATION
Acrescente o default: "solteiro":
Isso vai colocar por padrão (se o usuário não marcar nada) o enum como: "solteiro". Todos os campos que estão em branco ficarão com esse status automaticamente.
Execute no prompt:
rails db:migrate
Explicando melhor as linhas da migration
Sobre o add_column:
3 - Anatomia do enum no MODEL
Vamos configurar agora o nosso enum no model.
Bem, a "anatomia" do Enum do tipo string no Rails 8 é tipo essa:
enum :nome_do_seu_enum, {
chave1: "valor1",
chave2: "valor2,
chave3: "valor3"
}
As bases para se chegar a essa 'anatomia' estão aqui (no artigo do Michał Rudzki) e aqui (na documentação do Rails 8).
Aplicando o enum ao nosso cado aqui vai ficar assim:
enum :marital_status, {
solteiro: "solteiro",
casado: "casado",
divorciado: "divorciado",
viuvo: "viuvo",
uniao_estavel: "uniao_estavel"
}
Há algo interessante que podemos aplicar a esse enum também para facilitar a nossa vida... o "prefix: true". Mas para quê ele serve?
3.1 - O que o "prefix: true" faz?
Ele adiciona um prefixo com o nome do atributo aos métodos gerados pelo enum.
Sem prefixo, o Rails criaria métodos assim:
customer.solteiro?
customer.casado?
customer.viuvo!
Com prefix: true, eles viram:
customer.marital_status_solteiro?
customer.marital_status_casado?
customer.marital_status_viuvo!
Com prefix: true:
- tudo fica explícito
- mais legível em projetos grandes
- menos bugs “fantasma”
3.2 - Adicionando validações
Vamos acrescentar as seguintes validações ao nosso enum:
validates :marital_status, presence: true, inclusion: { in: marital_statuses.keys }
Onde cada validação diz o seguinte:
- validates - Rails valide para mim a seguinte estrutura
- :marital_status - A coluna a ser validada
- presence: true - Torna o preenchimento daquela coluna obrigatório, ou seja, não pode ser enviado ao banco de dados como nulo (nil), não pode ir em branco.
- inclusion: - Valida que o valor do atributo está dentro de uma lista específica de valores permitidos.
- marital_statuses.keys - Retorna um array com as chaves que poderão ser aceitas: ["solteiro", "casado", "divorciado", "viuvo", "uniao_estavel"].
Ou seja, o "presence" impede valores vazios e o "inclusion" impede valores inválidos, que não correspondam aos valores previamente passados.
4 - Permita a coluna no CONTROLLER
No Rails existe um conceito chamado: Strong Parameters.
Esse Strong Parameters quer dizer basicamente: Nenhum dado vindo do usuário entra no model se o programador do projeto não permitir explicitamente (isso por uma questão de segurança).
Ou seja, se você não for ao método "user_params" e não colocar o "marital_status" explicitamente lá, o Rails simplesmente vai ignorar esse campo e seguir pleníssimo sem enviar essa informação para o banco da dados.
Por isso, vamos acrescentar a coluna 'marital_status' ao nosso método 'user_params':
5 - Trabalhando com o enum nas VIEWs
Vamos precisar mexer em duas partials (fragmentos de view reutilizáveis):
- Uma que diz respeito ao formulário que será aplicado ao usuário (_form.html.erb)
- Outra que diz respeito a exibição daquele usuário depois de cadastrado (_user.html.erb)
Prensando primeiro no select (menu) que faremos para o usuário escolher o seu status civil no formulário...
5.1 - Lidando com o enum no _form.html.erb
No enum atualmente temos os seguintes valores para os estados civis:
enum :marital_status, {
solteiro: "solteiro",
casado: "casado",
divorciado: "divorciado",
viuvo: "viuvo",
uniao_estavel: "uniao_estavel"
}, prefix: true
Como podem ver, teremos um sério desconforto caso esses valores forem exibidos no menu para o usuário dessa forma. Isso porque:
- União Estável possui espaço e acentos
- Viúvo também possui acento
- O ideal é que os status civis viessem com letra maiúscula
- Os usuários serão de ambos os gêneros, logo, o ideal seria colocar a partícula (a) na intenção de contemplar a ambos.
Para resolver isso ao renderizar a partial sair com os valores bonitinhos no menu.
5.1.1 - Criando a constante que vai receber o hash com a tradução
Façamos o seguinte, vamos ao model user.rb:
app > models : user.rb
Uma vez que estejamos no model, vamos criar uma constante que irá conter um hash (chave/valor) de dados contendo as chaves que estão do jeitinho lá do enum e os valores traduzidos:
MARITAL_STATUS_TRANSLATIONS = {
"solteiro" => "Solteiro(a)",
"casado" => "Casado(a)",
"divorciado" => "Divorciado(a)",
"viuvo" => "Viúvo(a)",
"uniao_estavel" => "União Estável"
}
Pronto agora já temos uma 'tradução' com as perfumarias que queremos exibir!
Ok, mas como vamos aplicar isso a partial?
5.1.2 - Entendendo o helper options_for_select
Vamos a documentação
Para montar o nosso select (menu com os enums) vamos utilizar o helper options_for_select (ele é do tipo Form Options Helper). Vamos dar uma olhadinha na documentação dele:
Ele aceita diversas opções de entrada: hash, array e enumerável. Vamos utilizar essa primeira onde ele aceita receber arrays com chave/valor. Repare que:
- O Primeiro elemento - É o texto que o usuário vê (ex: "Dollar")
- O Segundo elemento - É o valor a ser salvo no banco (ex: "\$")
Então o array que vamos gerar precisará ter os primeiros elementos como sendo o valores bonitinhos e os segundos elementos os valores corretos do enum que são os que de fato vão para o banco. Vamos lá criá-lo entao...
5.1.3 - Criando método no model que vai nos ajudar na view
Vamos ao user.rb (app > models : user.rb) e neste criaremos um método de classe - ou seja, um método que pertence à classe em si. Que não precisa criar um objeto, chama direto na classe - para facilitar a nossa vida na hora de montar o select lá na view e deixar o codigo da view mais limpo e independente:
def self.marital_status_to_select
marital_statuses.keys.map { |values|
[MARITAL_STATUS_TRANSLATIONS[values], values]
}
end
Onde:
- self. - indica que é um método de classe, não de instância
-
marital_statuses.keys - pega todas as chaves do enum como array:
["solteiro", "casado", "divorciado", "viuvo", "uniao_estavel"] - .map { |values| ... } - itera sobre cada valor
-
[MARITAL_STATUS_TRANSLATIONS[values], values] - cria um array com 2 elementos:
- Primeiro elemento: texto que o usuário vê (ex: "Solteiro(a)")
- Segundo elemento: valor salvo no banco (ex: "solteiro")
Com isso, teremos o seguinte retorno:
[
["Solteiro(a)", "solteiro"],
["Casado(a)", "casado"],
["Divorciado(a)", "divorciado"],
["Viúvo(a)", "viuvo"],
["União Estável", "uniao_estavel"]
]
Perfeito, agora vamos lá as views aplicá-lo a partial do formulário...
5.1.4 - Aplicando o método de classe na partial
<div>
<%= form.label :marital_status, style: "display: block" %>
<%= form.select :marital_status,
options_for_select(
User.marital_status_to_select
)
%>
</div>
5.1.5 - Deixando opção pré-selecionada em caso de edição
A partial _form.html.erb pode ser utilizada tanto no cadastro de um usuário novo como na edição de um usuário já existente.
Levando em consideração essa segunda possibilidade utilizaremos o form.object.marital_status. Ele é usado para pré-selecionar a opção correta no dropdown quando você está editando um usuário existente.
A coisa toda vai ficar assim:
<div>
<%= form.label :marital_status, style: "display: block" %>
<%= form.select :marital_status,
options_for_select(
User.marital_status_to_select,
form.object.marital_status
)
%>
</div>
5.2 - Lidando com o enum no _user.html.erb
Para além de dar a opções bonitinhas, bem legíveis e amigáveis lá para a pessoa na hora de marcar no select (menu) do formulário, devemos prover também ao usuário poder ver o seu status matrimonial de maneira linda e amigável depois que preencher e enviar o nosso formulário.
Para isso é que vamos mexer na partial _user.html.erb . Mas antes precisamos configurar algo no model dos usuários.
5.2.1 - Criando o método para a tradução no Model
Vamos até o user.rb (Ctrl + p para fazer a busca rápida).
Uma vez no model criaremos neste um método de instância (ou seja, que pertence a um objeto específico (uma instância da classe) para fazer essa tradução bonitinha pro usuário na hora que ele for ver o cadastro que acabou de criar.
Onde:
- marital_status - Esse marital_status é o valor do atributo da instância atual do User. Ou seja, ele vem da coluna marital_status e não do enum.
- MARITAL_STATUS_TRANSLATIONS[...] - Usa esse valor como chave no hash de traduções
Um exemplo do que acontece ao aplicar esse método:
- marital_status retorna "solteiro"
- MARITAL_STATUS_TRANSLATIONS["solteiro"] busca no hash
- Retorna "Solteiro(a)"
5.2.2 - Modificando na partial _user.html.erb
Uma vez que você esteja na partial, basta aplicar o método ao objeto de user e vòi la vai ficar com o textinho na tela!!
<div>
<strong>Marital Status:</strong>
<%= user.marital_status_humanized %>
</div>
6 - Possibilidades de melhoria
Obviamente o conteúdo que cá está não esgota a temática dos enums no Rails, e nem pretende fazê-lo! Existem inúmeros tópicos que poderiam ser debatidos ainda nos enums, como por exemplo:
- A aplicação do I18n para internacionalização e adaptação a outros idiomas.
- A remoção do 'default' da migration podendo, consequentemente, colocar um placeholder no select
- Falar de scopes
- Entre outros temas...








Top comments (0)