Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?


Failed to load latest commit information.

Save migrations and columns by storing multiple booleans in a single integer.
e.g. true-false-false = 1, false-true-false = 2, true-false-true = 5 (1,2,4,8,..)

class User < ActiveRecord::Base
  include Bitfields
  bitfield :my_bits, 1 => :seller, 2 => :insane, 4 => :sensible

user = true, insane: true)
user.seller # => true
user.sensible? # => false
user.my_bits # => 3
  • records bitfield_changes user.bitfield_changes # => {"seller" => [false, true], "insane" => [false, true]} (also seller_was / seller_change / seller_changed? / seller_became_true? / seller_became_false?)
    • Individual added methods (i.e, seller_was, seller_changed?, etc..) can be deactivated with bitfield ..., added_instance_methods: false
    • Note: ActiveRecord 5.2 changes the behavior of _was and _changed? methods when used in the context of an after_save callback.
      • ActiveRecord 5.1 will use the use the values that were just changed.
      • ActiveRecord 5.2, however, will return the current value for _was and false for _changed? since the previous changes have been persisted.
  • adds scopes User.seller.sensible.first (deactivate with bitfield ..., scopes: false)
  • builds sql User.bitfield_sql(insane: true, sensible: false) # => '(users.my_bits & 6) = 1'
  • builds sql with OR condition User.bitfield_sql({ insane: true, sensible: true }, query_mode: :bit_operator_or) # => '(users.my_bits & 2) = 2 OR (users.bits & 4) = 4'
  • builds index-using sql with bitfield ... , query_mode: :in_list and User.bitfield_sql(insane: true, sensible: false) # => 'users.my_bits IN (2, 3)' (2 and 1+2) often slower than :bit_operator sql especially for high number of bits
  • builds update sql User.set_bitfield_sql(insane: true, sensible: false) == 'my_bits = (my_bits | 6) - 4'
  • faster sql than any other bitfield lib through combination of multiple bits into a single sql statement
  • gives access to bits User.bitfields[:my_bits][:sensible] # => 4
  • converts hash to bits User.bitfield_bits(seller: true) # => 1


gem install bitfields


ALWAYS set a default, bitfield queries will not work for NULL

t.integer :my_bits, default: 0, null: false
# OR
add_column :users, :my_bits, :integer, default: 0, null: false

Instance Methods

Global Bitfield Methods

Method Name Example (user = true, insane: true) Result
bitfield_values user.bitfield_values {"seller" => true, "insane" => true, "sensible" => false}
bitfield_changes user.bitfield_changes {"seller" => [false, true], "insane" => [false, true]}

Individual Bit Methods

Model Getters / Setters

Method Name Example (user = Result
#{bit_name} user.seller false
#{bit_name}= user.seller = true true
#{bit_name}? user.seller? true

Dirty Methods:

Some, not all, ActiveRecord::AttributeMethods::Dirty and ActiveModel::Dirty methods can be used on each bitfield:

Before Model Persistence
Method Name Example (user = Result
#{bit_name}_was user.seller_was false
#{bit_name}_in_database user.seller_in_database false
#{bit_name}_change user.seller_change [false, true]
#{bit_name}_change_to_be_saved user.seller_change_to_be_saved [false, true]
#{bit_name}_changed? user.seller_changed? true
will_save_change_to_#{bit_name}? user.will_save_change_to_seller? true
#{bit_name}_became_true? user.seller_became_true? true
#{bit_name}_became_false? user.seller_became_false? false
After Model Persistence
Method Name Example (user = User.create(seller: true)) Result
#{bit_name}_before_last_save user.seller_before_last_save false
saved_change_to_#{bit_name} user.saved_change_to_seller [false, true]
saved_change_to_#{bit_name}? user.saved_change_to_seller? true
  • Note: These methods are dynamically defined for each bitfield, and function separately from the real ActiveRecord::AttributeMethods::Dirty/ActiveModel::Dirty methods. As such, generic methods (e.g. attribute_before_last_save(:attribute)) will not work.


Update all users

User.seller.not_sensible.update_all(User.set_bitfield_sql(seller: true, insane: true))

Delete the shop when a user is no longer a seller

before_save :delete_shop, if: -> { |u| u.seller_change == [true, false] }

List fields and their respective values

user = true)
user.bitfield_values(:my_bits) # => { seller: false, insane: true, sensible: false }


  • [Upgrading] in version 0.2.2 the first field(when not given as hash) used bit 2 -> add a bogus field in first position
  • [Defaults for new records] set via db migration or name the bit foo_off to avoid confusion, setting via after_initialize does not work
  • It is slow to do: #{bitfield_sql(...)} AND #{bitfield_sql(...)}, merge both into one hash
  • bit_operator is faster in most cases, use query_mode: :in_list sparingly
  • Standard mysql integer is 4 byte -> 32 bitfields
  • If you are lazy or bad at math you can also do bitfields :bits, :foo, :bar, :baz
  • If you are want more readability and reduce clutter you can do bitfields 2**0 => :foo, 2**1 => :bar, 2**32 => :baz

Query-mode Benchmark

The query_mode: :in_list is slower for most queries and scales miserably with the number of bits.
Stay with the default query-mode. Only use :in_list if your edge-case shows better performance.


Testing With RSpec

To assert that a specific flag is a bitfield flag and has the active?, active, and active= methods and behavior use the following matcher:

require 'bitfields/rspec'

describe User do
  it { should have_a_bitfield :active }


  • convenient named scope User.with_bitfields(xxx: true, yyy: false)



Michael Grosser
License: MIT
Build Status


n Booleans = 1 Integer, saves columns and migrations.






No packages published