Class: Safe::Flag

Inherits:
Object
  • Object
show all
Defined in:
lib/enums/flag.rb,
lib/enums/flag_builder.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, value) ⇒ Flag

Returns a new instance of Flag.



13
14
15
16
17
18
# File 'lib/enums/flag.rb', line 13

def initialize( key, value )
  @key   = key
  @value = value
  self.freeze
  self
end

Instance Attribute Details

#keyObject (readonly)

Returns the value of attribute key.



10
11
12
# File 'lib/enums/flag.rb', line 10

def key
  @key
end

#valueObject (readonly)

Returns the value of attribute value.



11
12
13
# File 'lib/enums/flag.rb', line 11

def value
  @value
end

Class Method Details

._typecast_flag!(o) ⇒ Object



28
29
30
31
32
33
# File 'lib/enums/flag.rb', line 28

def self._typecast_flag!( o )
  if o.is_a? Symbol   ## auto-convert symbol to flag
    o = key( o )  ## lookup symbol in "parent" flags class
  end
  _typecheck_flag!( o )
end

._typecheck_flag!(o) ⇒ Object



20
21
22
23
24
25
26
# File 'lib/enums/flag.rb', line 20

def self._typecheck_flag!( o )
  if o.instance_of?( self )
    o
  else
    raise TypeError.new( "[Flag] flag >#{name}< type expected; got >#{o.class.inspect}<" )
  end
end

.allObject



133
# File 'lib/enums/flag.rb', line 133

def self.all() self::ALL; end

.build(value) ⇒ Object



121
122
123
124
125
# File 'lib/enums/flag.rb', line 121

def self.build( value )
  o = self.value( value )   # lookup if value is a known flag (with key)
  o = new( :"#{'%08b' % value}", value )  if o.nil?
  o
end

.build_class(class_name, *args, **kwargs) ⇒ Object Also known as: new

meta-programming “macro” - build class (on the fly)



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
77
# File 'lib/enums/flag_builder.rb', line 7

def self.build_class( class_name, *args, **kwargs )

  if args.size > 0
    keys   = args
    values = (0...keys.size).to_a   # note: use ... (exclusive) range
    values = values.map { |value| 1<<value }   ## use power of twos (e.g. 2^0, 2^1, etc.)
    f = Hash[ keys.zip( values ) ]
  else
    ## assume kwargs
    f = kwargs
  end


  ## todo/fix:
  ##  check class name MUST start with uppercase letter

  ## check if all keys are symbols and follow the ruby id(entifier) naming rules
  f.keys.each do |key|
    if key.is_a?( Symbol ) && key =~ /\A[a-z][a-zA-Z0-9_]*\z/
    else
      raise ArgumentError.new( "[Flag] arguments to Flag.new must be all symbols following the ruby id naming rules; >#{key}< failed" )
    end
  end

  klass = Class.new( Flag )
  ## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
  klass.define_singleton_method( :new ) do |*new_args|
    old_new( *new_args )
  end


  f.each do |key,value|
    klass.class_eval( <<RUBY )
      #{key.upcase} = new( :#{key}, #{value} )

      def self.#{key}
        #{key.upcase}
      end

      def #{key}?
        @value & #{value} == #{value}
      end
RUBY
  end

  klass.class_eval( <<RUBY )
    NONE = new( :none, 0 )
    ALL  = new( :all, #{f.values.reduce( 0 ) { |sum, value| sum|value }} )

    def self.members
      @members ||= [#{f.keys.map {|key|key.upcase}.join(',')}].freeze
    end
RUBY

  ## note: use Kernel for "namespacing"
  ##   make all enums Kernel convenience converters (always) global
  ##     including uppercase methods (e.g. State(), Color(), etc.) does NOT work otherwise (with other module includes)

  ## add global convenience converter function
  ##  e.g. State(0) is same as State.convert(0)
  ##       Color(0) is same as Color.convert(0)
  Kernel.class_eval( <<RUBY )
    def #{class_name}( arg )
       #{class_name}.convert( arg )
    end
RUBY

  ## note: use Safe (module) and NO Object for namespacing
  ##   use include Safe to make all enums constants and machinery global
  Safe.const_set( class_name, klass )   ## returns klass (plus sets global constant class name)
end

.convert(*args) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/enums/flag.rb', line 137

def self.convert( *args )
  if args.size == 0
    self::NONE
  elsif args.size == 1 && args[0].is_a?(Integer)
    build( args[0] )
  else
    ## assume flag object or symbols
    value = 0
    args.each do |arg|
      flag = _typecast_flag!( arg )
      value |= flag.value
    end
    build( value )
  end
end

.key(key) ⇒ Object Also known as: []



98
99
100
101
102
# File 'lib/enums/flag.rb', line 98

def self.key( key )
  # note: does NOT include :none (and :all)
  @hash_by_key ||= Hash[ keys.zip( members ) ].freeze
  @hash_by_key[key]
end

.keysObject



93
94
95
96
# File 'lib/enums/flag.rb', line 93

def self.keys()
  # note: does NOT include :none (and :all)
  @keys ||= members.map { |member| member.key }.freeze
end

.noneObject



131
# File 'lib/enums/flag.rb', line 131

def self.none() self::NONE; end

.value(value) ⇒ Object



114
115
116
117
118
119
# File 'lib/enums/flag.rb', line 114

def self.value( value )
  ## note: adds :none and :all for value lookup
  @hash_by_value ||= Hash[ values.zip( members ) ].merge( self::NONE.value => self::NONE,
                                                          self::ALL.value  => self::ALL ).freeze
  @hash_by_value[value]
end

.valuesObject



109
110
111
112
# File 'lib/enums/flag.rb', line 109

def self.values()
  # note: does NOT include :none (and :all)
  @values ||= members.map { |member| member.value }.freeze
end

.zeroObject



128
# File 'lib/enums/flag.rb', line 128

def self.zero() @zero ||= self::NONE; end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



40
41
42
43
44
45
46
# File 'lib/enums/flag.rb', line 40

def ==( other )
  if other.is_a?( Integer ) && other == 0   ## note: only allow compare by zero (0) integer - why? why not?
    @value == 0
  else
    @value == _typecheck_flag!( other ).value
  end
end

#_typecast_flag!(o) ⇒ Object



36
# File 'lib/enums/flag.rb', line 36

def _typecast_flag!( o ) self.class._typecast_flag!( o ); end

#_typecheck_flag!(o) ⇒ Object



35
# File 'lib/enums/flag.rb', line 35

def _typecheck_flag!( o ) self.class._typecheck_flag!( o ); end

#all?Boolean

Returns:

  • (Boolean)


134
# File 'lib/enums/flag.rb', line 134

def all?() @value & self.class::ALL.value == self.class::ALL.value; end

#bitwise_and(other) ⇒ Object Also known as: &



61
62
63
# File 'lib/enums/flag.rb', line 61

def bitwise_and( other )
  self.class.build( @value & _typecheck_flag!( other ).value )
end

#bitwise_inverseObject Also known as: ~



71
72
73
# File 'lib/enums/flag.rb', line 71

def bitwise_inverse
   self.class.build( ~@value )
end

#bitwise_or(other) ⇒ Object Also known as: |



56
57
58
# File 'lib/enums/flag.rb', line 56

def bitwise_or( other )
  self.class.build( @value | _typecheck_flag!( other ).value )
end

#bitwise_xor(other) ⇒ Object Also known as: ^



66
67
68
# File 'lib/enums/flag.rb', line 66

def bitwise_xor( other )
  self.class.build( @value ^ _typecheck_flag!( other ).value )
end

#member?(other) ⇒ Boolean

Returns:

  • (Boolean)


50
51
52
53
# File 'lib/enums/flag.rb', line 50

def member?( other )
  other = _typecast_flag!( other )
  @value & other.value == other.value
end

#none?Boolean

Returns:

  • (Boolean)


132
# File 'lib/enums/flag.rb', line 132

def none?() @value == 0; end

#parse_boolObject Also known as: to_bool

nonzero == true, zero == false like numbers



161
# File 'lib/enums/flag.rb', line 161

def parse_bool() @value != 0; end

#set(other) ⇒ Object Also known as: flag

note: typecast also allows symbol e.g (:read_only, etc.)



77
78
79
# File 'lib/enums/flag.rb', line 77

def set( other )  ## note: typecast also allows symbol e.g (:read_only, etc.)
  self.class.build( @value | _typecast_flag!( other ).value )
end

#to_bObject



160
# File 'lib/enums/flag.rb', line 160

def to_b()       parse_bool(); end

#to_iObject

add to_i, to_int - why? why not?



157
# File 'lib/enums/flag.rb', line 157

def to_i()       @value; end

#to_intObject

allows Integer( .. )



158
# File 'lib/enums/flag.rb', line 158

def to_int()     @value; end

#toggle(other) ⇒ Object



87
88
89
# File 'lib/enums/flag.rb', line 87

def toggle( other )
  self.class.build( @value ^ _typecast_flag!( other ).value )
end

#unset(other) ⇒ Object Also known as: unflag



82
83
84
# File 'lib/enums/flag.rb', line 82

def unset( other )
  self.class.build( @value & ~_typecast_flag!( other ).value )
end

#zero?Boolean

Returns:

  • (Boolean)


129
# File 'lib/enums/flag.rb', line 129

def zero?() @value == 0; end