Code Monkey home page Code Monkey logo

active_flag's Introduction

ActiveFlag - Bit array for ActiveRecord

Build Status

Store up to 64 multiple flags (bit array) in a single integer column with ActiveRecord. From a UI standpoint, it can be used as a multi-select checkbox storage.

Perfect solution to store multiple boolean values such as preferences, notification settings, achievement status, profile options, etc. in a single column.

  • Single column to group multiple boolean values. You don't need to have many separate columns. You don't even need a migration when you add a new flag item to the list.
  • Fast bitwise operations. WHERE languages & 3 > 0 is faster than WHERE (english = true) OR (spanish = true) OR ...

If you want a simple enum column, take a look at EnumAccessor.

If you need to work with huge bit arrays, take a look at Bitwise.

Usage

class Profile < ActiveRecord::Base
  flag :languages, [:english, :spanish, :chinese, :french, :japanese]
  # {:english=>1, :spanish=>2, :chinese=>4, :french=>8, :japanese=>16 }
  # OR you can specify values explicitly for future safety & convinience
  flag :languages, { english: 1, spanish: 2, chinese: 4, french: 8, japanese: 16 }
end

# Instance methods
profile.languages                           #=> #<ActiveFlag::Value: {:english, :japanese}>
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]

profile.languages = [:spanish, :japanese]   # Direct assignment that works with forms

# Class methods
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]

# Scope methods
Profile.where_languages(:french, :spanish)  #=> SELECT * FROM profiles WHERE languages & 10 > 0
Profile.where_all_languages(:french, :spanish)  #=> SELECT * FROM profiles WHERE languages & 10 = 10
Profile.where_not_languages(:french, :spanish)  #=> SELECT * FROM profiles WHERE languages & 10 = 0
Profile.where_not_all_languages(:french, :spanish)  #=> SELECT * FROM profiles WHERE languages & 10 < 10
Profile.languages.set_all!(:chinese)        #=> UPDATE "profiles" SET languages = COALESCE(languages, 0) | 4
Profile.languages.unset_all!(:chinese)      #=> UPDATE "profiles" SET languages = COALESCE(languages, 0) & ~4

Install

gem 'active_flag'

Migration

It is recommended to set 0 by default.

t.integer :languages,                    null: false, default: 0, limit: 8
# OR
add_column :users, :languages, :integer, null: false, default: 0, limit: 8

limit: 8 is only required if you need more than 32 flags.

Query

For a querying purpose, use where_[column], where_all_[column], where_not_[column] and where_not_all_[column] scopes.

Profile.where_languages(:french)            #=> SELECT * FROM profiles WHERE languages & 8 > 0

Also takes multiple values.

Profile.where_languages(:french, :spanish)  #=> SELECT * FROM profiles WHERE languages & 10 > 0

By default, it returns profiles that have either French or Spanish.

To get profiles that have both French and Spanish, use:

Profile.where_all_languages(:french, :spanish) #=> SELECT * FROM profiles WHERE languages & 10 = 10

To get profiles that do not have either French or Spanish, use:

Profile.where_not_languages(:french, :spanish) #=> SELECT * FROM profiles WHERE languages & 10 = 0

To get profiles that do not have both French and Spanish, use:

Profile.where_not_all_languages(:french, :spanish) #=> SELECT * FROM profiles WHERE languages & 10 < 10

Translation

ActiveFlag supports i18n just as ActiveModel does.

For instance, create a Japanese translation in config/locales/ja.yml

ja:
  active_flag:
    profile:
      languages:
        english: 英語
        spanish: スペイン語
        chinese: **語
        french: フランス語
        japanese: 日本語

and now to_human method returns a translated string.

I18n.locale = :ja
profile.languages.to_human  #=> ['英語', 'スペイン語']

I18n.locale = :en
profile.languages.to_human  #=> ['English', 'Spanish']

Forms

Thanks to the translation support, forms just work as you would expect with the pairs convenience method.

# With FormBuilder

= form_for(@profile) do |f|
  = f.collection_check_boxes :languages, Profile.languages.pairs

# With SimpleForm

= simple_form_for(@profile) do |f|
  = f.input :languages, as: :check_boxes, collection: Profile.languages.pairs

Other solutions

There are plenty of gems that share the same goal. However they have messy syntax than necessary in my opinion, and I wanted a better API to achieve that goal.

Also, ActiveFlag has one of the simplest code base that you can easily reason about or hack on.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.