Class: ActiveRecord::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/ar_binary_flag_attributes.rb

Class Method Summary collapse

Class Method Details

.binary_attributes(attributes, flags_attribute = :flags) ⇒ Object

Allows you to conserve space by storing several boolean attributes to one integer attribute.

class Building < ActiveRecord::Base
  flag_attributes(
    "overdriven"                => 0b00000001,
    "with_points"               => 0b00000010
  )
end

or something shorter

class Building < ActiveRecord::Base
  binary_attributes :overdriven, :with_points
end

b = Building.new
b.overdriven = true
b.overdriven? => true
b.with_points? => false
b.with_points = true
b.flags => 3


31
32
33
34
35
36
37
38
# File 'lib/ar_binary_flag_attributes.rb', line 31

def self.binary_attributes(attributes, flags_attribute = :flags)
  hash_attr = Hash.new
  attributes.each_with_index do |key, i|
    hash_attr[key] = 2 ** i
  end

  flag_attributes(hash_attr, flags_attribute)
end

.flag_attributes(attributes, flags_attribute = :flags) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/ar_binary_flag_attributes.rb', line 40

def self.flag_attributes(attributes, flags_attribute = :flags)
  # Calculate the full mask to be able to unset bits.
  full_mask = attributes.inject(0) { |full_mask, (_, mask)| full_mask | mask }

  get_flags = flags_attribute.to_sym
  set_flags = :"#{flags_attribute}="

  attributes.each do |method_name, mask|
    get_method = :"#{method_name}?"
    set_method = :"#{method_name}="

    # Check if bit is set in mask.
    define_method(get_method) { send(get_flags) & mask == mask }
    alias_method method_name, get_method

    # Set bit value.
    define_method(set_method) do |value|
      # rails fix
      if value and not value.to_s == '0'
        # Set bit in flags. This is done using or operation:
        #
        # 0b010101 |     0b010101 |
        # 0b001000 =     0b010000 =
        # 0b011101       0b010101
        send(set_flags, send(get_flags) | mask)
      else
        # Unset bit in flags. First we invert the full mask:
        #
        # 0b111111 ^
        # 0b000100 =
        # 0b111011
        #
        # Then we clear the bit:
        #
        # 0b010101 &
        # 0b111011 =
        # 0b010001
        send(set_flags, send(get_flags) & (full_mask ^ mask))
      end
      self
    end
  end
end