Module: BanditMask::Banditry

Defined in:
lib/banditmask/banditry.rb

Instance Method Summary collapse

Instance Method Details

#bandit_mask(attribute, as:, with: BanditMask) ⇒ Object

Creates wrapper methods for reading, writing, and querying the bitmask stored in attribute using the class with. with defaults to BanditMask, but you can (and probably should) define your own class descending from BanditMask to fill this role. The name of the accessor methods will be derived from as, e.g., if as is :foo, the reader method will be :foo and the writer method will be :foo=, and the query method will be :foo?.

The reader method will call BanditMask#bits to get an array of the enabled bit names represented by attribute.

The writer method will overwrite the current bitmask with an Integer representation of a new BanditMask built up using BanditMask#<<.

In addition to the accessor methods, a query method that delegates to BanditMask#include? will be added.

class ThingMask < BanditMask
  bit :read, 0b001
  bit :write, 0b010
  bit :execute, 0b100
  # ...
end

class Thing
  attr_accessor :bitmask

  extend BanditMask::Banditry
  bandit_mask :bitmask, as: :bits, with: ThingMask
end

37
38
39
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
# File 'lib/banditmask/banditry.rb', line 37

def bandit_mask(attribute, as:, with: BanditMask)
  cls = with
  wrapper = as

  class_eval do
    if respond_to? wrapper
      raise MethodCollisionError, "method `#{wrapper}` already exists"
    end

    if respond_to? :"#{wrapper}="
      raise MethodCollisionError, "method `#{wrapper}=` already exists"
    end

    if respond_to? :"#{wrapper}?"
      raise MethodCollisionError, "method `#{wrapper}?` already exists"
    end

    ##
    # A reader method which instances a new BanditMask object and calls
    # BanditMask#bits.
    define_method wrapper do
      cls.new(send(attribute)).bits
    end

    ##
    # A writer method which overwrites the current bitmask attribute with
    # the bitmask representation of the given bits.
    define_method :"#{wrapper}=" do |bits|
      mask = bits.reduce(cls.new) { |mask, bit| mask << bit }
      send :"#{attribute}=", Integer(mask)
    end

    ##
    # A query method which returns +true+ if each of the bits in +bits+ is
    # enabled in the current bitmask and +false+ otherwise.
    define_method :"#{wrapper}?" do |*bits|
      cls.new(send(attribute)).include? *bits
    end
  end
end