Skip to content
n Booleans = 1 Integer, saves columns and migrations.
Ruby
Latest commit e3af8f9 Mar 9, 2016 @grosser bump rspec
Failed to load latest commit information.
benchmark use sql_no_cache for tests Jan 20, 2011
gemfiles fix specs Feb 25, 2016
lib v0.7.0 Feb 25, 2016
spec bump rspec Mar 9, 2016
.travis.yml faster builds + ruby 1.9 breaks travis ... so no support Feb 25, 2016
Gemfile cleanup Jan 11, 2015
Gemfile.lock bump rspec Mar 10, 2016
Rakefile
Readme.md thx for the patch Feb 25, 2016
bitfields.gemspec faster builds + ruby 1.9 breaks travis ... so no support Feb 26, 2016

Readme.md

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 => :stupid
end

user = User.new(foo: true, insane: true)
user.seller == true
user.stupid? == false
user.my_bits == 3
  • records bitfield_changes user.bitfield_changes == {foo: [false, true]} (also foo_was / foo_change / foo_changed? / foo_became_true?)
  • adds scopes User.foo.stupid.first (deactivate with bitfield ..., scopes: false)
  • builds sql User.bitfield_sql(insane: true, stupid: false) == '(users.my_bits & 3) = 1'
  • builds index-using sql with bitfield ... , query_mode: :in_list and User.bitfield_sql(insane: true, stupid: 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, stupid: 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][:stupid] == 4

Install

gem install bitfields

Migration

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

Examples

Update all users

User.seller.not_stupid.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 = User.new(insane: true)
user.bitfield_values(:my_bits) == { seller: false, insane: true, stupid: false }

TIPS

  • [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] afaik it is not possible to have some bits true by default (without monkeypatching AR/see tests) -> choose a good naming like xxx_on / xxx_off to use the default 'false'
  • Never 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.

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 }
end

TODO

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

Authors

Contributors

Michael Grosser
michael@grosser.it
License: MIT
Build Status

Something went wrong with that request. Please try again.