Class: BitMagic::Adapters::Magician

Inherits:
Object
  • Object
show all
Defined in:
lib/bit_magic/adapters/magician.rb

Overview

A container for bit_magic settings and fields Meant to be used internally by the Base adapter (and by extension, all adapters).

Constant Summary collapse

DEFAULT_ACTION_OPTIONS =
Bits::DEFAULT_OPTIONS.merge({
:query_by_value => true, # can be true, false, or a number of bits
:helpers => true, # TODO: enable deeper access control over injected helper methods
:bits_wrapper => Bits,
:bits_generator => BitsGenerator,
}).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, options = {}) ⇒ Object

Initialize a new Magician, a container for bit_magic settings and fields

Examples:

Initialize a Magician (usually you would not do this directly)

magician = Magician.new(:example, 0 => :is_odd, [1, 2] => :eyes, 3..6 => :fingers, default: 7)
# here, field names are :is_odd, :eyes, and :fingers
# with bits indices 0, [1, 2], and [3, 4, 5, 6] respectively
# default is an action option, set to 7

Parameters:

  • name (Symbol)

    the name to be used as a namespace

  • options (Hash) (defaults to: {})

    the options given to bit_magic. Keys that are Integer, Arrays or Range objects are treated as bit allocations, their value should be the name of the bit field. Keys that are anything else (usually Symbol) are treated as action options or settings.



98
99
100
101
102
# File 'lib/bit_magic/adapters/magician.rb', line 98

def initialize(name, options = {})
  @bit_magic_name = name
  @field_options, @action_options = self.class.options_splitter(options)
  validate_field_options!
end

Instance Attribute Details

#action_optionsHash (readonly)

bit_magic options that define settings

Returns:

  • (Hash)

    the current value of action_options



12
13
14
# File 'lib/bit_magic/adapters/magician.rb', line 12

def action_options
  @action_options
end

#bit_magic_nameSymbol (readonly)

the name given to bit_magic

Returns:

  • (Symbol)

    the current value of bit_magic_name



12
13
14
# File 'lib/bit_magic/adapters/magician.rb', line 12

def bit_magic_name
  @bit_magic_name
end

#bits_lengthInteger (readonly)

total number of bits defined in field_options

Returns:

  • (Integer)

    the current value of bits_length



12
13
14
# File 'lib/bit_magic/adapters/magician.rb', line 12

def bits_length
  @bits_length
end

#field_listHash (readonly)

name => bits key pairs based off field_options

Returns:

  • (Hash)

    the current value of field_list



12
13
14
# File 'lib/bit_magic/adapters/magician.rb', line 12

def field_list
  @field_list
end

#field_optionsHash (readonly)

bit_magic options that define fields

Returns:

  • (Hash)

    the current value of field_options



12
13
14
# File 'lib/bit_magic/adapters/magician.rb', line 12

def field_options
  @field_options
end

#max_bitInteger (readonly)

the largest bit defined in field_options

Returns:

  • (Integer)

    the current value of max_bit



12
13
14
# File 'lib/bit_magic/adapters/magician.rb', line 12

def max_bit
  @max_bit
end

Class Method Details

.field_key_value_check(check) ⇒ Integer+

Checks if the key is a a field key definition (Integer or Range, defining bit or bit ranges in question).

Parameters:

  • checkValue (Integer, Range<Integer>)

    an integer bit or bit range to check if it is a valid bit index

Returns:

  • (Integer, Array<Integer>)

    will return an integer or an array of integers if the checkValue is a valid bit index or bit range



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/bit_magic/adapters/magician.rb', line 29

def self.field_key_value_check(check)
  if check.is_a?(Integer)
    check
  elsif check.is_a?(Range)
    list = check.to_a
    valid = list.reduce(true) {|m, i| m && i.is_a?(Integer) }
    list if valid and list.length > 0
  elsif check.is_a?(Array)
    valid = true
    list = check.collect {|i|
      key = self.field_key_value_check(i)
      key.nil? ? (valid = false; break;) : key
    }.flatten
    list if valid and list.length > 0
  end
end

.options_splitter(options = {}) ⇒ Hash

Separate field and action options from the options passed to bit_magic

Valid keys for field options are:

Integer - for a specific bit position
Range - for a range of bits
Array<Integer> - an array of bit positions

The value of the key-pair should be a Symbol.

Action options are everything else that’s not a field option

Parameters:

  • options (Hash) (defaults to: {})

    the options passed to bit_magic

Returns:

  • (Hash, Hash)

    returns an array of two options [field, actions] field options and action options, in that order respectively



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/bit_magic/adapters/magician.rb', line 60

def self.options_splitter(options = {})
  field_opts = {}
  action_opts = DEFAULT_ACTION_OPTIONS.dup
  
  options.each_pair do |check_key, value|
    if key = self.field_key_value_check(check_key)
      field_opts[key] = value
    else
      if (!options[:allow_failed_fields]) and (check_key.is_a?(Integer) or check_key.is_a?(Range) or check_key.is_a?(Array))
        raise BitMagic::FieldError.new("key-pair expected to be a valid field option, but it is not: #{check_key.inspect} => #{value.inspect}. If this is an action option, you can disable this error by passing ':allow_failed_fields => true' as an option")
      end
      action_opts[check_key] = value
    end
  end
  
  [field_opts.freeze, action_opts.freeze]
end

Instance Method Details

#bitsArray<Integer>

List of bits defined in @field_options

Returns:

  • (Array<Integer>)

    an array of bits defined in @field_options



175
176
177
# File 'lib/bit_magic/adapters/magician.rb', line 175

def bits
  @field_list.values.flatten
end

#bits_generatorBitsGenerator

Used by adapters to generate value lists for particular bit operations

Returns:

  • (BitsGenerator)

    a BitsGenerator object with this magician’s field list



190
191
192
# File 'lib/bit_magic/adapters/magician.rb', line 190

def bits_generator
  @bits_generator ||= (self.action_options[:bits_generator] || BitsGenerator).new self
end

#bits_wrapperClass

Used by the Base#bit_magic to create a Bits wrapper around instances

Returns:

  • (Class)

    a Bits class



182
183
184
# File 'lib/bit_magic/adapters/magician.rb', line 182

def bits_wrapper
  self.action_options[:bits_wrapper] || Bits
end

#define_bit_magic_methods(klass) ⇒ Object

Define helper methods on the instance, namespaced to the name given in the bit_magic invocation.

This is an internal method, only meant to be used by the Base adapter to during its setup phase. Should not be used directly.

Methods that are defined is namespaced to the name during initialization. Referred to here as NAMESPACE. These methods are available on instances.

NAMESPACE - defined by Base adapter, returns a Bits wrapper
NAMESPACE_enabled?(*field_names) - checks if all the given field names
  or bit indices are enabled (value > 0)
NAMESPACE_disabled?(*field_names) - checks if all the given field names
  or bit indices are disabled (value == 0)

The following are helpers, defined based on field names during initialization Refered to here as ‘name’.

name - returns the value of the field
name=(new_value) - sets the value of the field to the new value
name? - available only if the field is a single bit, returns true if 
  the value of the bit is 1, or false if 0

Parameters:

  • klass (Class)

    the class to inject methods into.

Returns:

  • nothing useful.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/bit_magic/adapters/magician.rb', line 131

def define_bit_magic_methods(klass)
  names = @field_list
  bit_magic_name = @bit_magic_name
  
  klass.instance_eval do
  
    define_method(:"#{bit_magic_name}_enabled?") do |*fields|
      self.send(bit_magic_name).enabled?(*fields)
    end
  
    define_method(:"#{bit_magic_name}_disabled?") do |*fields|
      self.send(bit_magic_name).disabled?(*fields)
    end
  end
  
  if @action_options[:helpers]
  
    klass.instance_eval do
      names.each_pair do |name, bits|
        
        define_method(:"#{name}") do
          self.send(bit_magic_name)[name]
        end
        
        define_method(:"#{name}=") do |val|
          self.send(bit_magic_name)[name] = val
        end
        
        if bits.is_a?(Integer) or bits.length == 1
          define_method(:"#{name}?") do
            self.send(bit_magic_name)[name] == 1
          end
        end
        
      end
    end
  
  end
  
end

#inspectObject

Inspect this object. This is customized just to shorten the output to actually be readable.



196
197
198
# File 'lib/bit_magic/adapters/magician.rb', line 196

def inspect
  "#<#{self.class.to_s} name=#{@bit_magic_name} field_list=#{@field_list.inspect}>"
end