Class: Safe::Enum

Inherits:
Object
  • Object
show all
Defined in:
lib/enums/enum.rb,
lib/enums/enum_builder.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, value) ⇒ Enum

Returns a new instance of Enum.



12
13
14
15
16
17
# File 'lib/enums/enum.rb', line 12

def initialize( key, value )
  @key   = key
  @value = value
  self.freeze   ## make "immutable"
  self
end

Instance Attribute Details

#keyObject (readonly)

return a new Enum read-only class



9
10
11
# File 'lib/enums/enum.rb', line 9

def key
  @key
end

#valueObject (readonly)

Returns the value of attribute value.



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

def value
  @value
end

Class Method Details

._typecheck_enum!(o) ⇒ Object



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

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

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

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



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
# File 'lib/enums/enum_builder.rb', line 10

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

  if args.size > 0
    keys   = args
    values = (0...keys.size).to_a   # note: use ... (exclusive) range
    e = Hash[ keys.zip( values ) ]
  else
    ## assume kwargs
    e = 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
  e.keys.each do |key|
    if key.is_a?( Symbol ) && key =~ /\A[a-z][a-zA-Z0-9_]*\z/
    else
      raise ArgumentError.new( "[Enum] arguments to Enum.new must be all symbols following the ruby id naming rules; >#{key}< failed" )
    end
  end

  klass = Class.new( Enum )

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

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

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

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

  klass.class_eval( <<RUBY )
    def self.members
      @members ||= [#{e.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(arg) ⇒ Object



82
83
84
85
86
87
88
# File 'lib/enums/enum.rb', line 82

def self.convert( arg )
  ## todo/check: support keys too - why? why not?
  ## e.g. Color(0), Color(1)
  ##      Color(:red), Color(:blue) - why? why not?
  ## note: will ALWAYS look-up by (member) index and NOT by value (integer number value might be different!!)
  members[ arg ]
end

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



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

def self.key( key )
  ## note: returns nil now for unknown keys
  ##   use/raise IndexError or something - why? why not?
  @hash_by_key ||= Hash[ keys.zip( members ) ].freeze
  @hash_by_key[key]
end

.keysObject



42
43
44
45
# File 'lib/enums/enum.rb', line 42

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

.sizeObject Also known as: length



77
# File 'lib/enums/enum.rb', line 77

def self.size() keys.size; end

.value(value) ⇒ Object



65
66
67
68
69
70
# File 'lib/enums/enum.rb', line 65

def self.value( value )
  ## note: returns nil now for unknown values
  ##   use/raise IndexError or something - why? why not?
  @hash_by_value ||= Hash[ values.zip( members ) ].freeze
  @hash_by_value[value]
end

.valuesObject



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

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

.zeroObject



73
# File 'lib/enums/enum.rb', line 73

def self.zero() members[0]; end

Instance Method Details

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



29
30
31
32
33
34
35
# File 'lib/enums/enum.rb', line 29

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_enum!( other ).value
  end
end

#_typecheck_enum!(o) ⇒ Object



26
# File 'lib/enums/enum.rb', line 26

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

#parse_boolObject Also known as: to_bool

nonzero == true, zero == false like numbers



96
# File 'lib/enums/enum.rb', line 96

def parse_bool() @value != 0; end

#to_bObject



95
# File 'lib/enums/enum.rb', line 95

def to_b()       parse_bool(); end

#to_iObject

add to_i, to_int - why? why not?



92
# File 'lib/enums/enum.rb', line 92

def to_i()       @value; end

#to_intObject

allows Integer( .. )



93
# File 'lib/enums/enum.rb', line 93

def to_int()     @value; end

#zero?Boolean

note: use compare by identity (object_id) and NOT value e.g. 0

Returns:

  • (Boolean)


74
# File 'lib/enums/enum.rb', line 74

def zero?() self == self.class.zero; end