Class: Safe::Flag
- Inherits:
-
Object
- Object
- Safe::Flag
- Defined in:
- lib/enums/flag.rb,
lib/enums/flag_builder.rb
Instance Attribute Summary collapse
-
#key ⇒ Object
readonly
Returns the value of attribute key.
-
#value ⇒ Object
readonly
Returns the value of attribute value.
Class Method Summary collapse
-
.[](key) ⇒ Object
convenience alias for key.
-
.build_class(class_name, *args, **kwargs) ⇒ Object
(also: new)
meta-programming “macro” - build class (on the fly).
- .convert(*args) ⇒ Object
- .key(key) ⇒ Object
- .keys ⇒ Object
- .values ⇒ Object
- .zero ⇒ Object
Instance Method Summary collapse
- #==(other) ⇒ Object (also: #eql?)
- #_member?(other) ⇒ Boolean
- #_typecast_flag!(o) ⇒ Object
- #_typecheck_flag!(o) ⇒ Object
- #bitwise_and(other) ⇒ Object (also: #&)
- #bitwise_inverse ⇒ Object (also: #~)
- #bitwise_or(other) ⇒ Object (also: #|)
- #bitwise_xor(other) ⇒ Object (also: #^)
-
#initialize(*args) ⇒ Flag
constructor
A new instance of Flag.
- #member?(other) ⇒ Boolean
-
#set(other) ⇒ Object
(also: #flag)
note: typecast also allows symbol e.g (:read_only, etc.).
- #toggle(other) ⇒ Object
- #unset(other) ⇒ Object (also: #unflag)
- #zero? ⇒ Boolean
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
#key ⇒ Object (readonly)
Returns the value of attribute key.
10 11 12 |
# File 'lib/enums/flag.rb', line 10 def key @key end |
#value ⇒ Object (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 |
.keys ⇒ Object
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 |
.values ⇒ Object
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 |
.zero ⇒ Object
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
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_inverse ⇒ Object 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
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
131 |
# File 'lib/enums/flag.rb', line 131 def zero?() @value == 0; end |