A sequel plugin that allows the use of integer columns to represent a big number of flags.
Ruby
Latest commit 84f5463 Mar 16, 2015 @sdepold Bump to version: v1.3.0

README.md

sequel-bit-fields

This is a plugin for Sequel which allows the creation of bitfields. With bitfields you can define flags / booleans / bits /whatsoever for you model.

Build status

Build Status

Usage

Let's define a model:

class MyModel < Sequel::Model
  # generic:
  plugin :bit_fields, :the_respective_column, [ :the, :different, :flags, :or, :bits ]

  # real world:
  plugin :bit_fields, :status_bits, [ :started, :finished, :reviewed ]
end

And now we can play around with an instance of it:

model = MyModel.create

model.started?        # => false
model.started = true
model.started?        # => true
model.status_bits     # => 1

model.finished?       # => false
model.finished = true
model.finished?       # => true
model.status_bits     # => 3

And we might want to find instances:

# let's find all the finished instances
MyModel.finished(true).all

# let's find all unfinished instances
MyModel.finished(false).all

# let's find all the started and the finished instances
MyModel.started.finished.all

# if you need to overwrite the table name:
MyModel.where(MyModel.finished_sql(true, :table => '_my_tmp_model')).all

Get the declared columns

MyModel.bit_fields
=begin
{ :status_bits => [ {
  :name => :started,
  :description => "Description for 'started' not available."
}, {
  :name        => :finished,
  :description => "Description for 'finished' not available."
}, {
  :name        => :reviewed,
  :description => "Description for 'reviewed' not available."
} ] }
=end

MyModel.bit_fields( :status_bits )
=begin
[ {
  :name => :started,
  :description => "Description for 'started' not available."
}, {
  :name        => :finished,
  :description => "Description for 'finished' not available."
}, {
  :name        => :reviewed,
  :description => "Description for 'reviewed' not available."
} ]
=end

Get the indexes of the columns

MyModel.bit_field_indexes_for( :status_bits )
# => { :started => 1, :finished => 2, :reviewed => 4 }

Get the values of the declared columns:

model = MyModel.new
model.finished => true
model.bit_field_values_for( :status_bits )
# => {:started => false, :finished => true, :reviewed => false}

# or with a specific value only:
model.bit_field_values_for( :status_bits, true )
# => { :finished => true }

Get all bit fields of all models, which use the plugin

Sequel::Plugins::BitFields.bit_fields_for_models
=begin
{ 'MyModel' => { :status_bits => [ {
    :name => :started,
    :description => "Description for 'started' not available."
  }, {
    :name        => :finished,
    :description => "Description for 'finished' not available."
  }, {
    :name        => :reviewed,
    :description => "Description for 'reviewed' not available."
  } ] }
}
=end

Check if a bit has been changed

Please note that you will need to add the dirty plugin to your model, too.

class DirtyModel < Sequel::Model
  plugin :dirty
  plugin :bit_fields, :status_bits, [ :started, :finished, :reviewed ]
end

model = DirtyModel.create
model.bit_changed?(:finished) # false
model.finished = !model.finished
model.bit_changed?(:finished) # true

Scoping

As you might find yourself in the situation, where you would like to define multiple bit fields with the same name, you will notice, that the straightforward attempt will overwrite the methods of the previously defined bit fields. In order to fix this, you can pass a scope options:

class User < Sequel::Model
  plugin :bit_fields, :website_permission_bits,     [ :admin ], :scope => :website
  plugin :bit_fields, :iphone_app_permission_bits,  [ :admin ], :scope => :iphone
  plugin :bit_fields, :android_app_permission_bits, [ :admin ], :scope => :android
end

This will change the name of the bit fields:

User.new.website_admin?                         # false
User.new.iphone_admin?                          # false
User.new(:android_admin => true).android_admin? # true

Options

Version 1.0.0 introduced the possibility to describe fields. Here is how it looks like:

class User < Sequel::Model
  plugin :bit_fields, :some_bits, [{
    :name        => :checked,
    :description => "This bit fields states that the model has been checked by someone."
  }]
end

Assigning symbols to columns and multiple bit_field assignment

You may want to set a bit_field via the column name and pass it a symbol. Or you may find it useful to turn on multiple bit_fields at the same time.

Values that are not passed will be set to false. This behavior is useful if you want to set specific bit_fields and not have to set every bit_field.

Given the model:

class MyModel < Sequel::Model
  plugin :bit_fields, :roles, [:author,:contributor,:reader]
end

Let's create a user that is a reader and contributor:

user = MyModel.new
user.roles = [:contributor,:reader]

user.contributor? # true
user.reader?      # true
user.author?      # false

user.roles = :author

user.author?      # true
user.reader?      # false
user.contributor? # false

The table

If you are creating a new model from scratch:

DB = Sequel.sqlite

DB.create_table(:my_models) do
  primary_key :id, :auto_increment => true

  # let's use Bignum as it has more space :)
  # set the default to 0 and disallow null values
  Bignum :status_bits, :null => false, :default => 0
end

The migration

If you want to extend an existing model:

Sequel.migration do
  change do
    alter_table :users do
      add_column :permission_bits, Bignum, :default => 0, :null => false
    end
  end
end

Installation

# gem approach
gem install sequel-bit_fields

# bundler approach
# add this to your Gemfile
gem 'sequel-bit_fields'

Side notes

You should always declare the column with a default value of 0. Also NULL should be disabled / not allowed. Otherwise the plugin will fail hard!

License

Hereby released under MIT license.

Authors/Contributors