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(*args) ⇒ Flag

Returns a new instance of Flag.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/enums/flag.rb', line 13

def initialize( *args )
  fmt = "%08b"

  if args.size == 0
    @value = 0   ## same as new(0)

    @key   = :"#{fmt % @value}"   ## use :none for 0 key - why? why not?

  elsif args.size == 1 && args[0].is_a?(Integer)
    @value = args[0]
    @key   = :"#{fmt % @value}"   ## todo: lookup if value is a known flag (with key) - why? why not?

  elsif args.size == 2 && args[0].is_a?(Symbol) && args[1].is_a?(Integer)
    @key   = args[0]
    @value = args[1]
  else
    ## assume flag object or symbols

    @value = 0
    args.each do |arg|
      flag = _typecast_flag!( arg )
      @value |= flag.value
    end
    @key = :"#{fmt % @value}"    ## todo: lookup if value is a known flag (with key) - why? why not?

  end
  self.freeze  ## make "immutable" - should be a value object like an integer number!!!

  self # return self for chaining

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

.[](key) ⇒ Object

convenience alias for key



119
120
121
# File 'lib/enums/flag.rb', line 119

def self.[]( key )  ## convenience alias for key

  self.key( key )
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
# 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( "      \#{key.upcase} = new( :\#{key}, \#{value} )\n\n      def self.\#{key}\n        \#{key.upcase}\n      end\n\n      def \#{key}?\n        _member?( \#{key.upcase} )\n      end\n" )
  end

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

  ## 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( "    def \#{class_name}( arg )\n       \#{class_name}.convert( arg )\n    end\n" )

  ## 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



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

def self.convert( *args )
  new( *args )
end

.key(key) ⇒ Object



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

def self.key( key )
  @hash ||= Hash[ keys.zip( members ) ].freeze
  @hash[key]
end

.keysObject



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

def self.keys()
  # note: does NOT include none - why? why not?

  @keys ||= members.map { |member| member.key }.freeze
end

.valuesObject



124
125
126
127
# File 'lib/enums/flag.rb', line 124

def self.values()
  # note: does NOT include none - why? why not?

  @values ||= members.map { |member| member.value }.freeze
end

.zeroObject



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

def self.zero() @zero ||= new(0); end

Instance Method Details

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



55
56
57
58
59
60
61
62
63
# File 'lib/enums/flag.rb', line 55

def ==( other )
  if self.class == other.class
    @value == other.value
  elsif other.is_a?( Integer )  ## note: also allow compare by "plain" integer

    @value == other
  else
    false
  end
end

#_member?(other) ⇒ Boolean

Returns:

  • (Boolean)


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

def _member?( other ) @value & other.value == other.value; end

#_typecast_flag!(o) ⇒ Object



47
48
49
50
51
52
# File 'lib/enums/flag.rb', line 47

def _typecast_flag!( o )
  if o.is_a? Symbol   ## auto-convert symbol to flag

    o = self.class.key( o )  ## lookup symbol in "parent" flags class

  end
  _typecheck_flag!( o )
end

#_typecheck_flag!(o) ⇒ Object



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

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

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



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

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

#bitwise_inverseObject Also known as: ~



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

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

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



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

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

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



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

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

#member?(other) ⇒ Boolean

Returns:

  • (Boolean)


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

def member?( other ) _member?(_typecast_flag!( other )); end

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

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



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

def set( other )  ## note: typecast also allows symbol e.g (:read_only, etc.)

  self.class.new( @value | _typecast_flag!( other ).value )
end

#toggle(other) ⇒ Object



102
103
104
# File 'lib/enums/flag.rb', line 102

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

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



97
98
99
# File 'lib/enums/flag.rb', line 97

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

#zero?Boolean

Returns:

  • (Boolean)


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

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