Skip to content

Instantly share code, notes, and snippets.

@abicky
Last active July 6, 2017 00:21
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save abicky/af5cbe94253ed79b7a8556932834da43 to your computer and use it in GitHub Desktop.
# @example
# class Setting
# include BitfieldBindable
# bind_bitfield :feature_bits, :features, 0 => "foo", 1 => "bar"
#
# attr_accessor :feature_bits
# def initialize
# @feature_bits = 0
# end
# end
#
# setting = Setting.new
# setting.feature_bits #=> 0
# setting.features.to_h #=> { "foo" => false, "bar" => false }
# setting.features.enabled = ["foo", "bar"]
# setting.feature_bits #=> 3
#
# setting.features.disable("foo")
# setting.features.enabled?("foo") #=> false
# setting.features.enabled?("bar") #=> true
# setting.feature_bits #=> 2
#
# setting.features.enable("foo")
# setting.features.enabled?("foo") #=> true
# setting.features.enabled?("bar") #=> true
module BitfieldBindable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def bind_bitfield(attr_name, bound_name, definition)
define_method(bound_name) do
@__bound_bitfield ||= {}
@__bound_bitfield[bound_name] ||= Binder.new(self, attr_name, definition)
end
end
end
class Binder
def initialize(model, attr_name, definition)
@model = model
@attr_name = attr_name
@field_to_bit_ord = definition.invert
end
def enabled?(field)
raise ArgumentError, "Unknown field: #{field}" unless @field_to_bit_ord.has_key?(field)
enabled.include?(field)
end
def enabled
bitfield_value_to_fields(@model.public_send(@attr_name))
end
def enabled=(fields)
@model.public_send("#{@attr_name}=", fields_to_bitfield_value(fields))
end
def enable(*fields)
self.enabled = (enabled + fields).uniq
end
def disable(*fields)
self.enabled = enabled - fields
end
def to_h
@field_to_bit_ord.keys.each.with_object({}) do |field, h|
h[field] = enabled?(field)
end
end
private
def bitfield_value_to_fields(value)
@field_to_bit_ord.keys.select.with_index do |field, bit_ord|
value & (1 << bit_ord) != 0
end
end
def fields_to_bitfield_value(fields)
flag_value = 0
fields.each do |field|
raise ArgumentError, "Unknown field: #{item}" unless @field_to_bit_ord.has_key?(field)
flag_value |= (1 << @field_to_bit_ord[field])
end
flag_value
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment