Install ActiveFlag
masterAdd the gem to your Gemfile to use ActiveFlag in your ActiveRecord models.
gem 'active_flag'repository·master·Indexed 42 days ago
https://github.com/kenn/active_flagA bit array implementation for ActiveRecord that allows developers to store multiple boolean flags within a single integer column in a database. It provides a `flag` macro for model definitions, bitwise query scopes (e.g., `where_`, `where_all_`), bulk update methods, and integration with Rails i18n for human-readable flag labels.
Add the gem to your Gemfile to use ActiveFlag in your ActiveRecord models.
gem 'active_flag'When creating a migration for an ActiveFlag column, it is recommended to set the default value to 0 and ensure the column is null: false.
If you need to store more than 32 flags, you must specify limit: 8 (which allows up to 64 flags).
t.integer :languages, null: false, default: 0, limit: 8
# OR
add_column :users, :languages, :integer, null: false, default: 0, limit: 8ActiveFlag integrates with Rails i18n. You can define translations in your locale files following this structure:
# config/locales/ja.yml
ja:
active_flag:
profile:
languages:
english: 英語
spanish: スペイン語
# ...When translations are present, [column].to_human will return the translated strings based on the current I18n.locale.
ja:
active_flag:
profile:
languages:
english: 英語
spanish: スペイン語
chinese: 中国語
french: フランス語
japanese: 日本語Once defined, the flag column returns an ActiveFlag::Value object which provides several methods for inspecting and modifying flags:
[column].english?: Returns true if the specific flag is set.[column].set?(:flag): Returns true if the flag is set.[column].unset?(:flag): Returns true if the flag is NOT set.[column].set(:flag): Sets the flag.[column].unset(:flag): Unsets the flag.[column].raw: Returns the underlying integer value.[column].to_a: Returns an array of the active flag symbols.[column].to_human: Returns an array of human-readable/translated strings.[column] = [:flag1, :flag2]: Direct assignment (useful for forms).# Assuming Profile model from example
profile = Profile.new
profile.languages.english? #=> true
profile.languages.set?(:english) #=> true
profile.languages.unset?(:english) #=> false
profile.languages.set(:spanish)
profile.languages.unset(:japanese)
profile.languages.raw #=> 3
profile.languages.to_a #=> [:english, :spanish]
# Direct assignment
profile.languages = [:spanish, :japanese]You can perform bulk updates on the database using these class-level methods:
[Model].languages.set_all!(:flag): Sets the specified flag for all records (using bitwise OR).[Model].languages.unset_all!(:flag): Unsets the specified flag for all records (using bitwise AND NOT).# Sets the :chinese flag for all profiles
Profile.languages.set_all!(:chinese)
# Unsets the :chinese flag for all profiles
Profile.languages.unset_all!(:chinese)Use the flag macro within your model to define the bit array. Pass the column name and an array of symbols representing the flags.
class Profile < ActiveRecord::Base
flag :languages, [:english, :spanish, :chinese, :french, :japanese]
endThe model class provides utility methods to inspect the flag definitions:
[Model].languages.maps: Returns a hash mapping symbols to their bit integer values.[Model].languages.humans: Returns a hash mapping symbols to their human-readable names.[Model].languages.pairs: Returns a hash mapping human-readable names to symbols (ideal for form collections).[Model].languages.to_array(integer): Converts a raw integer back into an array of symbols.# Assuming Profile model from example
Profile.languages.maps #=> {:english=>1, :spanish=>2, :chinese=>4, :french=>8, :japanese=>16 }
Profile.languages.humans #=> {:english=>"English", :spanish=>"Spanish", :chinese=>"Chinese", :french=>"French", :japanese=>"Japanese"}
Profile.languages.pairs #=> {"English"=>:english, "Spanish"=>:spanish, "Chinese"=>:chinese, "French"=>:french, "Japanese"=>:japanese}
Profile.languages.to_array(3) #=> [:english, :spanish]ActiveFlag provides several scopes to perform bitwise queries on your database:
where_[column](:flag1, :flag2): Returns records that have any of the specified flags (OR logic).where_all_[column](:flag1, :flag2): Returns records that have all of the specified flags (AND logic).where_not_[column](:flag1, :flag2): Returns records that have none of the specified flags.where_not_all_[column](:flag1, :flag2): Returns records that do not have all of the specified flags.Note: These scopes accept multiple arguments to perform bitwise operations on the combined flags.
# Returns profiles that have either French or Spanish
Profile.where_languages(:french, :spanish)
# Returns profiles that have both French and Spanish
Profile.where_all_languages(:french, :spanish)
# Returns profiles that do not have either French or Spanish
Profile.where_not_languages(:french, :spanish)
# Returns profiles that do not have both French and Spanish
Profile.where_not_all_languages(:french, :spanish)ActiveFlag provides several class-level scopes to query records based on their bitmask values. These scopes use bitwise AND operations in the underlying SQL.
where_<column>(*keys): Returns records that have at least one of the specified keys.where_all_<column>(*keys): Returns records that have all of the specified keys.where_not_<column>(*keys): Returns records that have none of the specified keys.where_not_all_<column>(*keys): Returns records that do not have all of the specified keys (i.e., they might have some, but not all).class User < ActiveRecord::Base
flag :roles, { admin: 1, editor: 2, viewer: 4 }
end
# Find users who are either admins or editors
User.where_roles(:admin, :editor)
# Find users who are both admins and editors
User.where_all_roles(:admin, :editor)
# Find users who are neither admins nor editors
User.where_not_roles(:admin, :editor)
# Find users who are not (both admin and editor)
User.where_not_all_roles(:admin, :editor)The to_i(arg) method converts one or more flag keys into their corresponding integer bitmask value. It accepts a single key or an Enumerable of keys. Keys are converted to symbols before lookup. If a key does not exist in the definition, it contributes 0 to the sum.
# Example of converting keys to a bitmask
mask = definition.to_i([:english, :spanish])To use ActiveFlag, call the flag macro within your ActiveRecord model. You must provide the database column name (which should be an integer type) and a hash of keys where the key is the label and the value is the bitwise integer value.
This macro generates:
where_ scopes for querying the bitmask.class User < ActiveRecord::Base
flag :roles, { admin: 1, editor: 2, viewer: 4 }
end
user = User.new
user.roles = [:admin, :viewer]
user.roles # => [:admin, :viewer]
user.roles_id # => 5 (the bitwise sum of 1 and 4)When you define a flag on a column, ActiveFlag generates a singleton method on the model class named after the pluralized column name. This method returns the ActiveFlag::Definition object for that column, allowing you to inspect the mapping or use it with scopes.
# If you define `flag :roles, ...`
# You can access the definition via `roles` on the class
User.roles # => #<ActiveFlag::Definition ...>