Usando bitmasks para armazenar configurações

Freqüentemente, precisamos armazenar vários booleanos para determinar as configurações de um usuário. Bitmasks são uma boa maneira econômica de fazer isso. A ideia é usar um único inteiro onde cada bit representa um desses booleanos. Isso é melhor do que salvar cada configuração individual em uma coluna diferente do banco de dados.

Escolha as posições dos bits para cada configuração. Digamos que queremos armazenar as configurações de notificação.

NEWSLETTER = 0
COMMENTS
= 1
FOLLOWER
= 2

Agora vamos desligar todas as configurações:

user.settings = 0

Agora vamos ativar as notificações de comentários:

user.settings |= (1 << COMMENTS)

Vamos ver como fica em binário:

user.settings.to_s(2)
>>> "10"

O bit na posição 1 ( COMMENTS) foi definido como 1, enquanto o resto não mudou.

A operação coloca (desloca) o valor 1 para a posição N de modo que, por exemplo:1 << N

 (1 << 4).to_s(2)
>>> "10000"

Agora |e &são os operadores OR e AND usuais, mas aplicados em um nível de bits para que:

(1 << 4 | 1 << 2).to_s(2)
>>> "10100"

Vemos que liga o bit N-posicionado no inteiro .a |= 1 << Na

O operador ~inverte todos os bits em uma variável para que:

(~0b1010).to_s(2)
>>> "-110"

Que são todos os bits definidos para 1, exceto bit 1 e bit 3, se falarmos de inteiros com sinal.

Para desligar um pouco, basta fazer

user.settings &= ~(1 << COMMENTS)

uma vez que terá todos os bits LIGADOS, exceto aquele na posição .~(1 << COMMENTS)COMMENTS

Para verificar se uma configuração específica está ATIVADA, basta verificar

user.settings & (1 << COMMENTS) > 0