Using a bitmask for options in Rails

Dusty Candland | | rails

Sometimes I like storing a set of options for a model as in integer in the DB, but still want a nice interface to use those options in code. This is one approach I've been using.

In this case the bitmask is call capabilities_mask, but could be whatever.


class AddCapabilitiesToPhoneNumbers < ActiveRecord::Migration[5.2]
def change
add_column :phone_numbers, :capabilities_mask, :integer, null: false, default: 0


This is the bulk of the code.

First we define the const with the values. This can be added do, but the order shouldn't change over time. Also, I'm using integers for the bitmasks, so they need to double for each new value.

Next we add methods to check for the value, dtmf?, for example will return true if the mask value contains dtmf. And dtmf! will add that option to the mask.

The capabilities setter & getter are mostly for display and updating from form, though they can be used for anywhere.

class PhoneNumber

dtmf: 1,
text_to_speech: 2,
agent_text: 4,
agent_voice: 8,

CAPABILITIES.each do |cap, bit|
define_method("#{cap}?") do
capabilities_mask & bit == bit

define_method("#{cap}!") do
self.capabilities_mask |= bit

def capabilities
CAPABILITIES.filter { |cap, bit| self.capabilities_mask & bit == bit }.map { |cap, bit| cap }

def capabilities= caps
self.capabilities_mask = Array.wrap(caps).filter(&:present?).map { |cap| CAPABILITIES[cap] }.reduce(0) { |m, b| m |= b }




Just need to allow the params since the model will handle setting the mask value.

  params.require(:phone_number).permit(:number, :name, :status, capabilities: [])


I use SimpleForm in most of my projects, so here's how to display a list of check boxes.

= f.input :capabilities, as: :check_boxes, collection:{|k,v| [k.to_s.humanize,k]}

In the show view.

strong Capabilities:
=<{|k, v| k.to_s.humanize}.join(", ")

Upsides / Downsides

Storing options this way only takes an interger column in the datbase, and potentially in API's. I usually use the text in API's though.

The biggest downside is the data isn't obvious in the DB and can be a pain to figure out an option, for example 11 would be dtmf, text_to_speech, and agent_voice in this example.


These are webmentions via the IndieWeb and Mention this post from your site: